Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
with:
node-version: 22
- run: npm ci
- run: npm ci
working-directory: apps/pr-reviewer/fixtures/demo-pr
- name: Export the PR diff
run: git diff origin/${{ github.base_ref }}...HEAD > fixtures/latest.diff
- name: Run the review agent
Expand Down
66 changes: 66 additions & 0 deletions apps/pr-reviewer/fixtures/demo-pr/cart-total.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, expect, it } from 'vitest';
import { cartTotal } from './cart-total.ts';

describe('cartTotal', () => {
it('returns 0 for an empty cart', () => {
expect(cartTotal([])).toBe(0);
});

it('returns the price of a single item with quantity 1', () => {
expect(cartTotal([{ price: 500, qty: 1 }])).toBe(500);
});

it('multiplies price by quantity', () => {
expect(cartTotal([{ price: 250, qty: 4 }])).toBe(1000);
});

it('sums several items', () => {
expect(
cartTotal([
{ price: 100, qty: 2 },
{ price: 300, qty: 1 },
{ price: 50, qty: 3 },
]),
).toBe(650);
});

it('counts the last item in the cart', () => {
expect(
cartTotal([
{ price: 1, qty: 1 },
{ price: 2, qty: 1 },
{ price: 4, qty: 1 },
]),
).toBe(7);
});

it('ignores items with quantity 0', () => {
expect(
cartTotal([
{ price: 999, qty: 0 },
{ price: 10, qty: 1 },
]),
).toBe(10);
});

it('rejects a negative quantity', () => {
expect(() => cartTotal([{ price: 100, qty: -1 }])).toThrow(RangeError);
});

it('names the offending item in the negative quantity error', () => {
expect(() =>
cartTotal([
{ price: 100, qty: 1 },
{ price: 100, qty: -2 },
]),
).toThrow('Negative quantity for item 1');
});

it('handles zero-priced items', () => {
expect(cartTotal([{ price: 0, qty: 5 }])).toBe(0);
});

it('handles large quantities', () => {
expect(cartTotal([{ price: 3, qty: 1_000_000 }])).toBe(3_000_000);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// These tests simulate the duration of the end-to-end checkout suite: each one
// makes a real assertion and then waits 60 seconds, as a browser-driven flow would.
import { describe, expect, it } from 'vitest';
import { cartTotal } from './cart-total.ts';
import { pointsFor } from './loyalty.ts';
import { canRefund } from './refund-window.ts';

const DAY_MS = 86_400_000;
const settle = () => new Promise((r) => setTimeout(r, 60_000));

describe('checkout flow', () => {
it('applies loyalty points across a multi-item cart', async () => {
const total = cartTotal([
{ price: 2500, qty: 2 },
{ price: 1000, qty: 1 },
]);
expect(pointsFor(total)).toBe(90);
await settle();
});

it('refund window closes after 30 days', async () => {
const now = new Date('2026-10-01T12:00:00Z');
expect(canRefund(new Date(now.getTime() - 30 * DAY_MS), now)).toBe(true);
expect(canRefund(new Date(now.getTime() - 31 * DAY_MS), now)).toBe(false);
await settle();
});

it('rejects a cart with a negative quantity before awarding points', async () => {
expect(() => cartTotal([{ price: 1000, qty: -1 }])).toThrow(RangeError);
await settle();
});

it('awards no points for an empty cart', async () => {
expect(pointsFor(cartTotal([]))).toBe(0);
await settle();
});

it('keeps a delivered order refundable while points are awarded', async () => {
const total = cartTotal([{ price: 4000, qty: 1 }]);
expect(pointsFor(total)).toBe(60);
expect(canRefund(new Date(), new Date())).toBe(true);
await settle();
});

it('rounds loyalty points once for an odd-cent order', async () => {
const total = cartTotal([
{ price: 199, qty: 1 },
{ price: 1, qty: 1 },
]);
expect(pointsFor(total)).toBe(3);
await settle();
});
});
44 changes: 44 additions & 0 deletions apps/pr-reviewer/fixtures/demo-pr/loyalty.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, expect, it } from 'vitest';
import { pointsFor } from './loyalty.ts';

describe('pointsFor', () => {
it('awards nothing for a zero total', () => {
expect(pointsFor(0)).toBe(0);
});

it('awards 1.5 points per whole dollar', () => {
expect(pointsFor(10_000)).toBe(150);
});

it('scales linearly for large totals', () => {
expect(pointsFor(20_000)).toBe(300);
});

it('rounds half a point up', () => {
expect(pointsFor(100)).toBe(2);
});

it('rounds once, after the multiplier', () => {
expect(pointsFor(199)).toBe(3);
});

it('awards nothing below a third of a dollar', () => {
expect(pointsFor(33)).toBe(0);
});

it('rounds 0.75 points up to 1', () => {
expect(pointsFor(50)).toBe(1);
});

it('awards nothing for a single cent', () => {
expect(pointsFor(1)).toBe(0);
});

it('handles odd cent totals', () => {
expect(pointsFor(12_345)).toBe(185);
});

it('returns a whole number', () => {
expect(Number.isInteger(pointsFor(777))).toBe(true);
});
});
Loading
Loading