CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 26s
CI / Build server (push) Successful in 28s
CI / Build client (push) Successful in 42s
CI / Playwright E2E tests (push) Successful in 1m43s
CI / Build and push Docker image (push) Successful in 48s
CI / Notify (push) Successful in 1s
347 lines
15 KiB
TypeScript
347 lines
15 KiB
TypeScript
import { resetMemoryStorage } from '../storage/memory';
|
||
import { getStores, addStore } from '../stores';
|
||
import { createGroup, deleteGroup, addGroupMember, removeGroupMember, updateGroupMember, setGroupState, setGroupTracking, markGroupMemberPaid, markGroupQrGenerated } from '../groups';
|
||
import { GroupState } from '../../../types/gen/types.gen';
|
||
|
||
const CREATOR = 'tomas';
|
||
const USER = 'petr';
|
||
const ADMIN_PW = 'testadmin';
|
||
const STORE = 'McDonald\'s';
|
||
const TODAY = new Date('2025-01-10');
|
||
|
||
beforeEach(async () => {
|
||
resetMemoryStorage();
|
||
process.env.ADMIN_PASSWORD = ADMIN_PW;
|
||
await addStore(STORE, ADMIN_PW);
|
||
});
|
||
|
||
afterEach(() => {
|
||
delete process.env.ADMIN_PASSWORD;
|
||
});
|
||
|
||
describe('createGroup', () => {
|
||
test('vytvoří skupinu, creator je člen', async () => {
|
||
const data = await createGroup(CREATOR, STORE, TODAY);
|
||
expect(data.groups).toHaveLength(1);
|
||
const group = data.groups![0];
|
||
expect(group.name).toBe(STORE);
|
||
expect(group.creatorLogin).toBe(CREATOR);
|
||
expect(group.state).toBe(GroupState.OPEN);
|
||
expect(group.members[CREATOR]).toBeDefined();
|
||
});
|
||
|
||
test('odmítne název mimo seznam obchodů', async () => {
|
||
await expect(createGroup(CREATOR, 'Neznámý obchod', TODAY)).rejects.toThrow('seznam');
|
||
});
|
||
|
||
test('vygeneruje unikátní ID', async () => {
|
||
const d1 = await createGroup(CREATOR, STORE, TODAY);
|
||
const d2 = await createGroup(USER, STORE, TODAY);
|
||
expect(d2.groups).toHaveLength(2);
|
||
expect(d2.groups![1].id).not.toBe(d2.groups![0].id);
|
||
});
|
||
|
||
describe('výběr URL na nabídku', () => {
|
||
const MULTI_STORE = 'Bistro';
|
||
const WOLT_URL = 'https://wolt.com/bistro';
|
||
const BOLT_URL = 'https://food.bolt.eu/bistro';
|
||
|
||
beforeEach(async () => {
|
||
await addStore(MULTI_STORE, ADMIN_PW, [WOLT_URL, BOLT_URL]);
|
||
});
|
||
|
||
test('uloží vybranou URL do skupiny', async () => {
|
||
const data = await createGroup(CREATOR, MULTI_STORE, TODAY, BOLT_URL);
|
||
expect(data.groups![0].url).toBe(BOLT_URL);
|
||
});
|
||
|
||
test('odmítne URL, která k obchodu nepatří', async () => {
|
||
await expect(createGroup(CREATOR, MULTI_STORE, TODAY, 'https://wolt.com/jine'))
|
||
.rejects.toThrow('nepatří');
|
||
});
|
||
|
||
test('bez výběru zůstane skupina bez URL, když má obchod více nabídek', async () => {
|
||
const data = await createGroup(CREATOR, MULTI_STORE, TODAY);
|
||
expect(data.groups![0].url).toBeUndefined();
|
||
});
|
||
|
||
test('dosadí jedinou URL obchodu automaticky', async () => {
|
||
await addStore('Pizzerie', ADMIN_PW, [WOLT_URL]);
|
||
const data = await createGroup(CREATOR, 'Pizzerie', TODAY);
|
||
expect(data.groups![0].url).toBe(WOLT_URL);
|
||
});
|
||
|
||
test('obchod bez URL vytvoří skupinu bez URL', async () => {
|
||
const data = await createGroup(CREATOR, STORE, TODAY);
|
||
expect(data.groups![0].url).toBeUndefined();
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('deleteGroup', () => {
|
||
test('creator může smazat skupinu', async () => {
|
||
const d = await createGroup(CREATOR, STORE, TODAY);
|
||
const groupId = d.groups![0].id;
|
||
const result = await deleteGroup(CREATOR, groupId, TODAY);
|
||
expect(result.groups).toHaveLength(0);
|
||
});
|
||
|
||
test('nečlen nemůže smazat skupinu', async () => {
|
||
const d = await createGroup(CREATOR, STORE, TODAY);
|
||
const groupId = d.groups![0].id;
|
||
await expect(deleteGroup(USER, groupId, TODAY)).rejects.toThrow('zakladatel');
|
||
});
|
||
|
||
test('smazání neexistující skupiny vyhodí chybu', async () => {
|
||
await expect(deleteGroup(CREATOR, 'nonexistent', TODAY)).rejects.toThrow('nalezena');
|
||
});
|
||
});
|
||
|
||
describe('addGroupMember', () => {
|
||
let groupId: string;
|
||
|
||
beforeEach(async () => {
|
||
const d = await createGroup(CREATOR, STORE, TODAY);
|
||
groupId = d.groups![0].id;
|
||
});
|
||
|
||
test('uživatel se může přidat sám (open)', async () => {
|
||
const d = await addGroupMember(USER, groupId, USER, TODAY);
|
||
expect(d.groups![0].members[USER]).toBeDefined();
|
||
});
|
||
|
||
test('creator může přidat jiného uživatele', async () => {
|
||
const d = await addGroupMember(CREATOR, groupId, USER, TODAY);
|
||
expect(d.groups![0].members[USER]).toBeDefined();
|
||
});
|
||
|
||
test('nečlen nemůže přidat jiného uživatele', async () => {
|
||
await expect(addGroupMember(USER, groupId, 'dalsi', TODAY)).rejects.toThrow('zakladatel');
|
||
});
|
||
|
||
test('nelze přidat do skupiny ve stavu ordered', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
await expect(addGroupMember(USER, groupId, USER, TODAY)).rejects.toThrow('objednáno');
|
||
});
|
||
|
||
test('nelze přidat existujícího člena', async () => {
|
||
await expect(addGroupMember(CREATOR, groupId, CREATOR, TODAY)).rejects.toThrow('již');
|
||
});
|
||
});
|
||
|
||
describe('removeGroupMember', () => {
|
||
let groupId: string;
|
||
|
||
beforeEach(async () => {
|
||
const d = await createGroup(CREATOR, STORE, TODAY);
|
||
groupId = d.groups![0].id;
|
||
await addGroupMember(CREATOR, groupId, USER, TODAY);
|
||
});
|
||
|
||
test('člen se může odhlásit sám', async () => {
|
||
const d = await removeGroupMember(USER, groupId, USER, TODAY);
|
||
expect(d.groups![0].members[USER]).toBeUndefined();
|
||
});
|
||
|
||
test('creator může odebrat jiného člena', async () => {
|
||
const d = await removeGroupMember(CREATOR, groupId, USER, TODAY);
|
||
expect(d.groups![0].members[USER]).toBeUndefined();
|
||
});
|
||
|
||
test('nelze odebrat zakladatele', async () => {
|
||
await expect(removeGroupMember(CREATOR, groupId, CREATOR, TODAY)).rejects.toThrow('Zakladatel');
|
||
});
|
||
|
||
test('nečlen nemůže odebrat jiného', async () => {
|
||
await expect(removeGroupMember(USER, groupId, CREATOR, TODAY)).rejects.toThrow('zakladatel');
|
||
});
|
||
});
|
||
|
||
describe('updateGroupMember', () => {
|
||
let groupId: string;
|
||
|
||
beforeEach(async () => {
|
||
const d = await createGroup(CREATOR, STORE, TODAY);
|
||
groupId = d.groups![0].id;
|
||
await addGroupMember(CREATOR, groupId, USER, TODAY);
|
||
});
|
||
|
||
test('člen může upravit svá data (open)', async () => {
|
||
const d = await updateGroupMember(USER, groupId, USER, { amount: 150, note: 'Big Mac' }, TODAY);
|
||
expect(d.groups![0].members[USER].amount).toBe(150);
|
||
expect(d.groups![0].members[USER].note).toBe('Big Mac');
|
||
});
|
||
|
||
test('creator může upravit data jiného člena', async () => {
|
||
const d = await updateGroupMember(CREATOR, groupId, USER, { amount: 200 }, TODAY);
|
||
expect(d.groups![0].members[USER].amount).toBe(200);
|
||
});
|
||
|
||
test('člen nemůže upravit data jiného (locked)', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await expect(updateGroupMember(USER, groupId, USER, { amount: 100 }, TODAY)).rejects.toThrow('uzamčeno');
|
||
});
|
||
|
||
test('nikdo nemůže upravit při stavu ordered', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
await expect(updateGroupMember(CREATOR, groupId, USER, { amount: 100 }, TODAY)).rejects.toThrow('objednáno');
|
||
});
|
||
});
|
||
|
||
describe('setGroupState', () => {
|
||
let groupId: string;
|
||
|
||
beforeEach(async () => {
|
||
const d = await createGroup(CREATOR, STORE, TODAY);
|
||
groupId = d.groups![0].id;
|
||
});
|
||
|
||
test('open → locked', async () => {
|
||
const d = await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
expect(d.groups![0].state).toBe(GroupState.LOCKED);
|
||
});
|
||
|
||
test('locked → open (odemčení)', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
const d = await setGroupState(CREATOR, groupId, GroupState.OPEN, TODAY);
|
||
expect(d.groups![0].state).toBe(GroupState.OPEN);
|
||
});
|
||
|
||
test('locked → ordered', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
const d = await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
expect(d.groups![0].state).toBe(GroupState.ORDERED);
|
||
});
|
||
|
||
test('open → ordered není povoleno', async () => {
|
||
await expect(setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY)).rejects.toThrow('Nelze přejít');
|
||
});
|
||
|
||
test('ordered → open není povoleno', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
await expect(setGroupState(CREATOR, groupId, GroupState.OPEN, TODAY)).rejects.toThrow('Nelze přejít');
|
||
});
|
||
|
||
test('ordered → delivered', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
const d = await setGroupState(CREATOR, groupId, GroupState.DELIVERED, TODAY);
|
||
expect(d.groups![0].state).toBe(GroupState.DELIVERED);
|
||
});
|
||
|
||
test('delivered → ordered (vrácení)', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.DELIVERED, TODAY);
|
||
const d = await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
expect(d.groups![0].state).toBe(GroupState.ORDERED);
|
||
});
|
||
|
||
test('locked → delivered není povoleno', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await expect(setGroupState(CREATOR, groupId, GroupState.DELIVERED, TODAY)).rejects.toThrow('Nelze přejít');
|
||
});
|
||
|
||
test('delivered → locked není povoleno', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.DELIVERED, TODAY);
|
||
await expect(setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY)).rejects.toThrow('Nelze přejít');
|
||
});
|
||
|
||
test('skupinu ve stavu delivered nelze upravovat', async () => {
|
||
await addGroupMember(CREATOR, groupId, USER, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.DELIVERED, TODAY);
|
||
await expect(updateGroupMember(CREATOR, groupId, USER, { amount: 100 }, TODAY)).rejects.toThrow('doručeno');
|
||
await expect(addGroupMember(CREATOR, groupId, 'dalsi', TODAY)).rejects.toThrow('doručeno');
|
||
});
|
||
|
||
test('nečlen nemůže měnit stav', async () => {
|
||
await expect(setGroupState(USER, groupId, GroupState.LOCKED, TODAY)).rejects.toThrow('zakladatel');
|
||
});
|
||
|
||
test('ordered → locked smaže sledování objednávky', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
const token = 'a'.repeat(64);
|
||
await setGroupTracking(CREATOR, groupId, `https://food.bolt.eu/sharedActiveOrder/${token}`, TODAY);
|
||
const d = await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
expect(d.groups![0].trackingCode).toBeUndefined();
|
||
expect(d.groups![0].trackingProvider).toBeUndefined();
|
||
});
|
||
});
|
||
|
||
describe('markGroupQrGenerated', () => {
|
||
let groupId: string;
|
||
|
||
beforeEach(async () => {
|
||
const d = await createGroup(CREATOR, STORE, TODAY);
|
||
groupId = d.groups![0].id;
|
||
});
|
||
|
||
test('ze stavu ordered přepne skupinu na delivered', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
const d = await markGroupQrGenerated(CREATOR, groupId, TODAY);
|
||
expect(d.groups![0].state).toBe(GroupState.DELIVERED);
|
||
expect(d.groups![0].qrGenerated).toBe(true);
|
||
});
|
||
|
||
test('funguje i ve stavu delivered', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.DELIVERED, TODAY);
|
||
const d = await markGroupQrGenerated(CREATOR, groupId, TODAY);
|
||
expect(d.groups![0].state).toBe(GroupState.DELIVERED);
|
||
});
|
||
|
||
test('ve stavu locked selže', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await expect(markGroupQrGenerated(CREATOR, groupId, TODAY)).rejects.toThrow('objednáno');
|
||
});
|
||
|
||
test('QR může generovat pouze zakladatel', async () => {
|
||
await setGroupState(CREATOR, groupId, GroupState.LOCKED, TODAY);
|
||
await setGroupState(CREATOR, groupId, GroupState.ORDERED, TODAY);
|
||
await expect(markGroupQrGenerated(USER, groupId, TODAY)).rejects.toThrow('zakladatel');
|
||
});
|
||
});
|
||
|
||
describe('markGroupMemberPaid', () => {
|
||
test('označí člena jako zaplaceného pro daný den', async () => {
|
||
const d = await createGroup(CREATOR, STORE, TODAY);
|
||
const groupId = d.groups![0].id;
|
||
const result = await markGroupMemberPaid(CREATOR, groupId, TODAY);
|
||
expect(result).not.toBeNull();
|
||
expect(result!.groups![0].members[CREATOR].paid).toBe(true);
|
||
});
|
||
|
||
test('vrátí null pro neexistující skupinu', async () => {
|
||
const result = await markGroupMemberPaid(CREATOR, 'nonexistent', TODAY);
|
||
expect(result).toBeNull();
|
||
});
|
||
|
||
// Regrese: potvrzení platby jiný den, než vznikla objednávka, se musí zapsat
|
||
// ke dni objednávky – jinak se příznak ztratí a zelené zatržítko se nezobrazí.
|
||
test('zapíše platbu ke dni objednávky i při potvrzení jiný den', async () => {
|
||
const orderDay = new Date('2025-01-10');
|
||
const d = await createGroup(CREATOR, STORE, orderDay);
|
||
const groupId = d.groups![0].id;
|
||
|
||
// Uživatel potvrzuje platbu jiný den, ale předáváme datum objednávky.
|
||
const result = await markGroupMemberPaid(CREATOR, groupId, orderDay);
|
||
expect(result).not.toBeNull();
|
||
expect(result!.groups![0].members[CREATOR].paid).toBe(true);
|
||
|
||
// Skupina pod jiným dnem neexistuje – ověříme, že tam příznak omylem nevznikl.
|
||
const otherDay = new Date('2025-01-15');
|
||
const onOtherDay = await markGroupMemberPaid(CREATOR, groupId, otherDay);
|
||
expect(onOtherDay).toBeNull();
|
||
});
|
||
});
|