Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.spice.profitmandi.web.offercircular;import static org.junit.Assert.assertEquals;import static org.junit.Assert.assertNotEquals;import static org.junit.Assert.assertTrue;import java.time.LocalDate;import org.junit.Test;import com.spice.profitmandi.web.offercircular.WebOfferSyncService.Line;import com.spice.profitmandi.web.offercircular.WebOfferSyncService.ModelOffer;/*** Guards the consolidated badge - the one offer line a partner reads on a product card.** Before consolidation a model carried one badge per circular row that named it; Edge 60* Pro 12+256 had five, and the partner had to reconcile them. These cases are taken from* the Sep'26 circular, because the failures that matter here are all real ones: a bank* that does not offer a scheme, a tenure that belongs to a different bank's offer, an* amount that stops earlier than the badge does.** Constructed with no collaborators - the rendering must not need Spring or a database.*/public class ModelOfferTest {private final WebOfferSyncService service = new WebOfferSyncService(null, null);private static final LocalDate SEP_1 = LocalDate.of(2026, 9, 1);private static final LocalDate SEP_16 = LocalDate.of(2026, 9, 16);private static final LocalDate SEP_20 = LocalDate.of(2026, 9, 20);private static final LocalDate SEP_26 = LocalDate.of(2026, 9, 26);private static Line cashback(String mode, String amount, String banks,String tenures, LocalDate end) {Line line = new Line();line.txnMode = mode;line.calcType = "FLAT";line.flatAmount = new java.math.BigDecimal(amount);line.timing = "INSTANT";line.banks = banks;line.tenures = tenures;line.start = SEP_1;line.end = end;return line;}private static Line scheme(String tenures, String banks) {Line line = new Line();line.timing = "INSTANT";line.banks = banks;line.tenures = tenures;line.start = SEP_1;line.end = SEP_20;return line;}private static ModelOffer model(String brand, Line... lines) {ModelOffer offer = new ModelOffer();offer.brand = brand;for (Line line : lines) {offer.add(line);}return offer;}// ------------------------------------------------------------------ Edge 60 Pro/*** Motorola Edge 60 Pro (12GB 256GB), Sep'26 page 9 - five badges before, one now.** The banks differ on every line, so none can be hoisted: the EMI cashback is* IDFC/Kotak, no-cost EMI is ICICI on 3 and 6 months but SBI on 3 only, and low-cost* is SBI. Stating one bank list for the badge would offer every bank every term.*/@Testpublic void consolidatesFiveBadgesIntoOneWithBanksPerLine() {ModelOffer offer = model("Motorola",cashback("CC_EMI", "2500", "IDFC First Bank, Kotak Mahindra Bank", null, SEP_20),cashback("CC_FULL_SWIPE", "2000", "IDFC First Bank, State Bank of India", null, SEP_20),scheme("NCE 3, 6 months", "ICICI Bank"),scheme("NCE 3 month", "State Bank of India"),scheme("LCE 6, 9, 12, 18, 24 months", "State Bank of India"));String title = service.modelTitle(offer);assertEquals("SEPTEMBER 2026 Motorola Cashback Offer-2,500 On Credit Card EMI"+ " & 2,000 On Credit Card Full Swipe", title);String detail = service.modelDetail(offer);assertTrue(detail, detail.contains("Rs.2,500 on Credit Card EMI with IDFC First Bank"));assertTrue(detail, detail.contains("NCE 3, 6 months with ICICI Bank"));assertTrue(detail, detail.contains("NCE 3 month with State Bank of India"));// no single trailing bank list, because no two lines share oneassertTrue(detail, !detail.contains("Banks: "));}// ------------------------------------------------------------------ bank hoisting/** When every line shares one bank set it is stated once, not on each line. */@Testpublic void hoistsTheBankListWhenEveryLineSharesIt() {ModelOffer offer = model("Samsung",cashback("CC_FULL_SWIPE", "3000", "HDFC Bank, ICICI Bank", null, SEP_20),cashback("CC_EMI", "3000", "HDFC Bank, ICICI Bank", "LCE 3, 6 months", SEP_20));String detail = service.modelDetail(offer);assertTrue(detail, detail.endsWith("Banks: HDFC Bank, ICICI Bank"));assertTrue(detail, !detail.contains("with HDFC Bank"));}// ------------------------------------------------------------------ "Upto"/*** "Upto" only where a mode carries more than one value - which is what happens when* banks pay differently for the same thing. A single value is stated plainly, or the* badge would understate a firm offer.*/@Testpublic void saysUptoOnlyWhenAModeHasSeveralValues() {assertTrue(service.modelTitle(model("Apple",cashback("CC_EMI", "6000", "Axis Bank", null, SEP_16),cashback("CC_EMI", "4000", "Axis Bank", null, SEP_26))).contains("Upto 6,000 On Credit Card EMI"));String single = service.modelTitle(model("Apple",cashback("CC_EMI", "6000", "Axis Bank", null, SEP_26)));assertTrue(single, single.contains("6,000 On Credit Card EMI"));assertTrue(single, !single.contains("Upto"));}// ------------------------------------------------------------------ windows/*** Apple 1025780, Sep'26: Rs.6,000 to the 16th, then Rs.4,000 to the 26th - the same* banks and the same mode, stepping down.** The badge spans 1-26 Sep, so the earlier amount MUST carry its own end date or the* badge advertises Rs.6,000 for ten days it is not available. The later amount runs to* the badge's own end and says nothing, which keeps the common case clean.*/@Testpublic void anAmountThatEndsEarlyStatesItsOwnValidity() {ModelOffer offer = model("Apple",cashback("CC_EMI", "6000", "Axis Bank", null, SEP_16),cashback("CC_EMI", "4000", "Axis Bank", null, SEP_26));assertEquals(SEP_1, offer.start);assertEquals(SEP_26, offer.end);String detail = service.modelDetail(offer);assertTrue(detail, detail.contains("Rs.6,000 on Credit Card EMI (till 16 Sep)"));assertTrue(detail, detail.contains("Rs.4,000 on Credit Card EMI"));assertTrue(detail, !detail.contains("Rs.4,000 on Credit Card EMI (till"));}// ------------------------------------------------------------------ ordering/** Highest value first, so the number a partner is looking for leads. */@Testpublic void statesHigherValuesFirst() {String detail = service.modelDetail(model("Motorola",cashback("CC_EMI", "1500", "SBI", null, SEP_20),cashback("CC_EMI", "2500", "SBI", null, SEP_20)));assertTrue(detail, detail.indexOf("Rs.2,500") < detail.indexOf("Rs.1,500"));}// ------------------------------------------------------------------ scheme-only/** A model with no cashback at all is titled by its scheme, never "Cashback Offer". */@Testpublic void aSchemeOnlyModelIsTitledByItsScheme() {String title = service.modelTitle(model("Motorola",scheme("NCE 3, 6 months", "State Bank of India")));assertEquals("SEPTEMBER 2026 Motorola NCE 3, 6 months", title);assertTrue(title, !title.contains("Cashback Offer"));assertTrue(title, !title.contains("NA"));}// ------------------------------------------------------------------ tenure origin/*** A tenure belongs to the offer that granted it, never to the model.** Edge 60 Pro's EMI cashback is IDFC/Kotak with NO tenure of its own, while its* schemes are ICICI's and SBI's. Attaching the model's tenures to that cashback would* promise no-cost EMI on a bank that never offered it.*/@Testpublic void doesNotAttachAnotherOffersTenuresToACashback() {String detail = service.modelDetail(model("Motorola",cashback("CC_EMI", "2500", "IDFC First Bank", null, SEP_20),scheme("NCE 3, 6 months", "ICICI Bank")));assertTrue(detail, detail.contains("Rs.2,500 on Credit Card EMI with IDFC First Bank"));assertTrue(detail, !detail.contains("Credit Card EMI (NCE"));}// ------------------------------------------------------------------ length/** detailedText is VARCHAR(1024); a model with many lines must not overflow it. */@Testpublic void neverExceedsTheColumnWidth() {ModelOffer offer = new ModelOffer();offer.brand = "Samsung";for (int i = 0; i < 40; i++) {offer.add(cashback("CC_EMI", String.valueOf(1000 + i),"HDFC Bank, ICICI Bank, State Bank of India, Axis Bank " + i, null, SEP_20));}assertTrue(service.modelDetail(offer).length() <= 1024);assertTrue(service.modelTitle(offer).length() <= 256);}}