| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- import java.time.LocalDateTime;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.List;
- import java.util.regex.Pattern;
- /** Standalone regression checks; does not connect to a database. */
- public final class RepairSkuDurationTest {
- private static int checks;
- public static void main(String[] args) {
- exactYearIsCandidateForBothSkus();
- calendarMonthArithmeticHandlesLeapDaysAndMonthEnds();
- timestampDifferencesRequireReview();
- missingDatesAndOtherDurationsAreSkipped();
- unrelatedSkusAreSkipped();
- renewalAndMultiplePurchasesRequireReview();
- quantityMeansDifferentThingsForLegacyAndSlotOrders();
- missingOrMismatchedPurchaseRequiresReview();
- adjustedAndSuspendedTicketsRequireReview();
- specialAcquisitionRequiresReview();
- generatedSqlMatchesTheOldSnapshot();
- nullableUpdatedSnapshotIsPreserved();
- System.out.println("RepairSkuDurationTest: " + checks + " checks passed");
- }
- private static void exactYearIsCandidateForBothSkus() {
- for (long skuId : new long[]{772L, 774L}) {
- RepairSkuDuration.Ticket ticket = ticket();
- ticket.skuId = skuId;
- RepairSkuDuration.Decision decision = classify(ticket);
- kind("candidate", decision, "exact year, SKU " + skuId);
- equal(ticket.expiry.minusMonths(6), decision.newExpiry,
- "candidate subtracts six calendar months from expiry");
- equal(0.0, decision.deltaSeconds, "exact year has no timestamp difference");
- }
- RepairSkuDuration.Ticket outside = ticket();
- outside.status = "outside";
- kind("candidate", classify(outside), "occupied extra seat is supported");
- }
- private static void calendarMonthArithmeticHandlesLeapDaysAndMonthEnds() {
- RepairSkuDuration.Ticket leap = ticket();
- leap.start = time("2024-02-29T10:20:30");
- leap.expiry = time("2025-02-28T10:20:30");
- RepairSkuDuration.Decision leapDecision = classify(leap);
- kind("candidate", leapDecision, "leap-day year uses calendar arithmetic");
- equal(time("2024-08-28T10:20:30"), leapDecision.newExpiry,
- "subtract from actual expiry; do not calculate start plus six months");
- RepairSkuDuration.Ticket monthEnd = ticket();
- monthEnd.start = time("2025-08-31T10:20:30");
- monthEnd.expiry = time("2026-08-31T10:20:30");
- RepairSkuDuration.Decision monthEndDecision = classify(monthEnd);
- kind("candidate", monthEndDecision, "month-end year is recognized");
- equal(time("2026-02-28T10:20:30"), monthEndDecision.newExpiry,
- "six-month subtraction clamps to the last day of February");
- RepairSkuDuration.Ticket leapFebruary = ticket();
- leapFebruary.start = time("2023-08-31T10:20:30");
- leapFebruary.expiry = time("2024-08-31T10:20:30");
- equal(time("2024-02-29T10:20:30"), classify(leapFebruary).newExpiry,
- "six-month subtraction preserves leap February's final day");
- }
- private static void timestampDifferencesRequireReview() {
- for (long seconds : new long[]{-1, 1, -30, 30}) {
- RepairSkuDuration.Ticket ticket = ticket();
- ticket.expiry = ticket.expiry.plusSeconds(seconds);
- RepairSkuDuration.Decision decision = classify(ticket);
- kind("review", decision, "timestamp difference " + seconds + " seconds");
- equal(Math.abs((double) seconds), Math.abs(decision.deltaSeconds),
- "report the timestamp difference accurately");
- }
- RepairSkuDuration.Ticket manualTime = ticket();
- manualTime.expiry = manualTime.expiry.toLocalDate().atStartOfDay();
- kind("review", classify(manualTime), "same anniversary date with a different time");
- }
- private static void missingDatesAndOtherDurationsAreSkipped() {
- RepairSkuDuration.Ticket noStart = ticket();
- noStart.start = null;
- kind("skipped", classify(noStart), "missing start time");
- RepairSkuDuration.Ticket noExpiry = ticket();
- noExpiry.expiry = null;
- kind("skipped", classify(noExpiry), "missing expiry time");
- RepairSkuDuration.Ticket noDates = ticket();
- noDates.start = null;
- noDates.expiry = null;
- kind("skipped", classify(noDates), "ticket has never been activated");
- for (int months : new int[]{1, 6, 18, 24}) {
- RepairSkuDuration.Ticket ticket = ticket();
- ticket.expiry = ticket.start.plusMonths(months);
- kind("skipped", classify(ticket), "nonannual duration " + months + " months");
- }
- }
- private static void unrelatedSkusAreSkipped() {
- for (long skuId : new long[]{773L, 775L, 1L}) {
- RepairSkuDuration.Ticket ticket = ticket();
- ticket.skuId = skuId;
- kind("skipped", classify(ticket), "unrelated SKU " + skuId);
- }
- }
- private static void renewalAndMultiplePurchasesRequireReview() {
- RepairSkuDuration.Ticket ticket = ticket();
- RepairSkuDuration.Purchase renewal = purchase(ticket);
- renewal.orderType = 2;
- kind("review", classify(ticket, Collections.singletonList(renewal), 0),
- "a renewal must not be mistaken for an incorrectly configured first purchase");
- RepairSkuDuration.Purchase first = purchase(ticket);
- RepairSkuDuration.Purchase second = purchase(ticket);
- second.id++;
- kind("review", classify(ticket, Arrays.asList(first, second), 0),
- "two purchases may legitimately add up to a year");
- second.orderType = 2;
- kind("review", classify(ticket, Arrays.asList(first, second), 0),
- "original purchase plus renewal requires review");
- }
- private static void quantityMeansDifferentThingsForLegacyAndSlotOrders() {
- RepairSkuDuration.Ticket ticket = ticket();
- RepairSkuDuration.Purchase purchase = purchase(ticket);
- purchase.quantity = 2;
- kind("review", classify(ticket, Collections.singletonList(purchase), 0),
- "legacy purchase quantity can extend one ticket's duration");
- purchase.slot = true;
- RepairSkuDuration.Decision decision = classify(ticket, Collections.singletonList(purchase), 0);
- kind("candidate", decision, "a successful delivery slot represents one ticket");
- equal(ticket.expiry.minusMonths(6), decision.newExpiry,
- "slot delivery subtracts six months once, regardless of order quantity");
- }
- private static void missingOrMismatchedPurchaseRequiresReview() {
- RepairSkuDuration.Ticket ticket = ticket();
- kind("review", classify(ticket, Collections.emptyList(), 0),
- "missing purchase does not prove that the annual duration is wrong");
- RepairSkuDuration.Purchase purchase = purchase(ticket);
- purchase.skuId = 775L;
- kind("review", classify(ticket, Collections.singletonList(purchase), 0),
- "current ticket SKU differs from the purchase SKU");
- }
- private static void adjustedAndSuspendedTicketsRequireReview() {
- RepairSkuDuration.Ticket ticket = ticket();
- kind("review", classify(ticket, Collections.singletonList(purchase(ticket)), 1),
- "manual adjustment history requires review even if the dates total one year");
- RepairSkuDuration.Ticket compensated = ticket();
- compensated.compensationDays = 3;
- kind("review", classify(compensated), "compensated ticket");
- RepairSkuDuration.Ticket frozen = ticket();
- frozen.frozen = true;
- kind("review", classify(frozen), "frozen ticket");
- RepairSkuDuration.Ticket recovering = ticket();
- recovering.recover = true;
- kind("review", classify(recovering), "ticket being restored");
- RepairSkuDuration.Ticket reserved = ticket();
- reserved.reservedDays = 15;
- kind("review", classify(reserved), "ticket with reserved duration");
- }
- private static void specialAcquisitionRequiresReview() {
- RepairSkuDuration.Ticket vip = ticket();
- vip.vip = true;
- kind("review", classify(vip), "VIP ticket");
- RepairSkuDuration.Ticket trial = ticket();
- trial.aqType = 2;
- kind("review", classify(trial), "trial acquisition");
- }
- private static void generatedSqlMatchesTheOldSnapshot() {
- RepairSkuDuration.Ticket ticket = ticket();
- String sql = normalizeSql(RepairSkuDuration.updateSql(ticket));
- containsPattern(sql, "\\bupdate\\s+groups_relation\\b", "SQL updates the ticket table");
- snapshotNumber(sql, "id", ticket.id);
- snapshotNumber(sql, "groups_id", ticket.groupsId);
- snapshotNumber(sql, "user_id", ticket.userId);
- snapshotNumber(sql, "sku_id", ticket.skuId);
- snapshotDate(sql, "start_time", "2026-01-15 10:20:30");
- snapshotDate(sql, "expiry_time", "2027-01-15 10:20:30");
- snapshotDate(sql, "update_time", "2026-01-15 10:21:40");
- containsPattern(sql, "\\bwhere\\b", "SQL contains a guarded WHERE clause");
- }
- private static void nullableUpdatedSnapshotIsPreserved() {
- RepairSkuDuration.Ticket ticket = ticket();
- ticket.updated = null;
- String sql = normalizeSql(RepairSkuDuration.updateSql(ticket));
- containsPattern(sql, "\\bupdate_time\\s*(?:is\\s+null|<=>\\s*null)\\b",
- "null update timestamp is compared as null");
- }
- private static RepairSkuDuration.Ticket ticket() {
- RepairSkuDuration.Ticket ticket = new RepairSkuDuration.Ticket();
- ticket.id = 880011L;
- ticket.groupsId = 660022L;
- ticket.skuId = 772L;
- ticket.userId = 440033L;
- ticket.status = "validity";
- ticket.start = time("2026-01-15T10:20:30");
- ticket.expiry = time("2027-01-15T10:20:30");
- ticket.updated = time("2026-01-15T10:21:40");
- ticket.aqType = 1;
- return ticket;
- }
- private static RepairSkuDuration.Purchase purchase(RepairSkuDuration.Ticket ticket) {
- RepairSkuDuration.Purchase purchase = new RepairSkuDuration.Purchase();
- purchase.id = 330044L;
- purchase.skuId = ticket.skuId;
- purchase.orderType = 1;
- purchase.quantity = 1;
- return purchase;
- }
- private static RepairSkuDuration.Decision classify(RepairSkuDuration.Ticket ticket) {
- return classify(ticket, Collections.singletonList(purchase(ticket)), 0);
- }
- private static RepairSkuDuration.Decision classify(RepairSkuDuration.Ticket ticket,
- List<RepairSkuDuration.Purchase> purchases,
- int adjustmentCount) {
- return RepairSkuDuration.classify(ticket, purchases, adjustmentCount);
- }
- private static LocalDateTime time(String value) {
- return LocalDateTime.parse(value);
- }
- private static void kind(String expected, RepairSkuDuration.Decision decision, String description) {
- equal(expected, decision.kind, description + "; reason=" + decision.reason);
- }
- private static void equal(Object expected, Object actual, String description) {
- checks++;
- if (expected == null ? actual != null : !expected.equals(actual)) {
- throw new AssertionError(description + ": expected=" + expected + ", actual=" + actual);
- }
- }
- private static String normalizeSql(String sql) {
- return sql.replace("`", "").replaceAll("\\s+", " ");
- }
- private static void snapshotNumber(String sql, String column, long value) {
- containsPattern(sql, "\\b" + column + "\\s*(?:=|<=>)\\s*" + value + "\\b",
- "SQL guards the original " + column);
- }
- private static void snapshotDate(String sql, String column, String value) {
- containsPattern(sql, "\\b" + column + "\\s*(?:=|<=>)\\s*'" + Pattern.quote(value)
- + "(?:\\.0+)?'", "SQL guards the original " + column);
- }
- private static void containsPattern(String value, String regex, String description) {
- checks++;
- if (!Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(value).find()) {
- throw new AssertionError(description + ": expected pattern=" + regex + ", SQL=" + value);
- }
- }
- }
|