import { expect, test } from "@playwright/test" import { ApiClient } from "../api-client" import { SEED } from "../seed-constants" const apiClient = new ApiClient() test.describe("KCanG Regulatory Edge Cases @full", () => { test.beforeEach(async () => { await apiClient.login(SEED.admin.email, SEED.admin.password) await apiClient.resetDb() }) // Requires: backend quota enforcement test("rejects adult distribution exceeding 25g/day", async ({ page }) => { await page.goto("/distributions/new") // Select adult member (Max Mustermann) const memberSelect = page .getByLabel(/mitglied|member/i) .or(page.locator("[data-testid='distribution-member-select']")) await memberSelect.click() await page.getByText(SEED.members.max.name).click() // Select strain const strainSelect = page .getByLabel(/sorte|strain|charge|batch/i) .or(page.locator("[data-testid='distribution-strain-select']")) await strainSelect.click() await page.getByText(SEED.strains.northernLights.name).click() // Enter 26g (exceeds 25g daily limit) const amountInput = page .getByLabel(/menge|amount|gramm/i) .or(page.locator("input[name*='amount']")) await amountInput.fill("26") // Submit const submitBtn = page.getByRole("button", { name: /ausgeben|submit|speichern/i, }) await submitBtn.click() // Should show rejection/error await expect( page.getByText(/überschr|exceeded|limit|abgelehnt|rejected/i) ).toBeVisible({ timeout: 5000 }) }) // Requires: backend quota enforcement test("accepts adult distribution of exactly 25g", async ({ page }) => { await page.goto("/distributions/new") const memberSelect = page .getByLabel(/mitglied|member/i) .or(page.locator("[data-testid='distribution-member-select']")) await memberSelect.click() await page.getByText(SEED.members.max.name).click() const strainSelect = page .getByLabel(/sorte|strain|charge|batch/i) .or(page.locator("[data-testid='distribution-strain-select']")) await strainSelect.click() await page.getByText(SEED.strains.northernLights.name).click() const amountInput = page .getByLabel(/menge|amount|gramm/i) .or(page.locator("input[name*='amount']")) await amountInput.fill("25") const submitBtn = page.getByRole("button", { name: /ausgeben|submit|speichern/i, }) await submitBtn.click() // Should succeed await expect( page.getByText(/erfolg|success|gespeichert/i) ).toBeVisible({ timeout: 5000 }) }) // Requires: backend quota enforcement test("rejects under-21 member with strain exceeding 10% THC", async ({ page, }) => { await page.goto("/distributions/new") // Select under-21 member (Jonas Weber) const memberSelect = page .getByLabel(/mitglied|member/i) .or(page.locator("[data-testid='distribution-member-select']")) await memberSelect.click() await page.getByText(SEED.members.jonas.name).click() // Select Amnesia Haze (22% THC — exceeds 10% limit for under-21) const strainSelect = page .getByLabel(/sorte|strain|charge|batch/i) .or(page.locator("[data-testid='distribution-strain-select']")) await strainSelect.click() await page.getByText(SEED.strains.amnesiaHaze.name).click() const amountInput = page .getByLabel(/menge|amount|gramm/i) .or(page.locator("input[name*='amount']")) await amountInput.fill("5") const submitBtn = page.getByRole("button", { name: /ausgeben|submit|speichern/i, }) await submitBtn.click() // Should show THC rejection await expect( page.getByText(/thc|überschr|exceeded|limit|abgelehnt|rejected/i) ).toBeVisible({ timeout: 5000 }) }) // Requires: backend quota enforcement test("accepts under-21 member with strain within THC limit", async ({ page, }) => { await page.goto("/distributions/new") // Select under-21 member (Jonas) const memberSelect = page .getByLabel(/mitglied|member/i) .or(page.locator("[data-testid='distribution-member-select']")) await memberSelect.click() await page.getByText(SEED.members.jonas.name).click() // Select CBD Critical Mass (5% THC — within 10% limit) const strainSelect = page .getByLabel(/sorte|strain|charge|batch/i) .or(page.locator("[data-testid='distribution-strain-select']")) await strainSelect.click() await page.getByText(SEED.strains.cbdCriticalMass.name).click() const amountInput = page .getByLabel(/menge|amount|gramm/i) .or(page.locator("input[name*='amount']")) await amountInput.fill("5") const submitBtn = page.getByRole("button", { name: /ausgeben|submit|speichern/i, }) await submitBtn.click() // Should succeed await expect( page.getByText(/erfolg|success|gespeichert/i) ).toBeVisible({ timeout: 5000 }) }) // Requires: backend quota enforcement test("rejects under-21 member exceeding 30g/month", async ({ page }) => { // This test assumes Jonas has already received close to 30g this month // The seed data should set up 31g attempted distribution await page.goto("/distributions/new") const memberSelect = page .getByLabel(/mitglied|member/i) .or(page.locator("[data-testid='distribution-member-select']")) await memberSelect.click() await page.getByText(SEED.members.jonas.name).click() const strainSelect = page .getByLabel(/sorte|strain|charge|batch/i) .or(page.locator("[data-testid='distribution-strain-select']")) await strainSelect.click() await page.getByText(SEED.strains.cbdCriticalMass.name).click() // 31g exceeds the 30g/month limit for under-21 const amountInput = page .getByLabel(/menge|amount|gramm/i) .or(page.locator("input[name*='amount']")) await amountInput.fill("31") const submitBtn = page.getByRole("button", { name: /ausgeben|submit|speichern/i, }) await submitBtn.click() // Should show monthly quota rejection await expect( page.getByText(/überschr|exceeded|limit|monat|monthly|abgelehnt/i) ).toBeVisible({ timeout: 5000 }) }) // Requires: backend quota enforcement test("accepts near-quota member within daily limit", async ({ page }) => { // Thomas has 23g already this day — 2g more should be fine (25g total) await page.goto("/distributions/new") const memberSelect = page .getByLabel(/mitglied|member/i) .or(page.locator("[data-testid='distribution-member-select']")) await memberSelect.click() await page.getByText(SEED.members.thomas.name).click() const strainSelect = page .getByLabel(/sorte|strain|charge|batch/i) .or(page.locator("[data-testid='distribution-strain-select']")) await strainSelect.click() await page.getByText(SEED.strains.northernLights.name).click() const amountInput = page .getByLabel(/menge|amount|gramm/i) .or(page.locator("input[name*='amount']")) await amountInput.fill("2") const submitBtn = page.getByRole("button", { name: /ausgeben|submit|speichern/i, }) await submitBtn.click() // Should succeed (23g + 2g = 25g, exactly at limit) await expect( page.getByText(/erfolg|success|gespeichert/i) ).toBeVisible({ timeout: 5000 }) }) // Requires: backend quota enforcement test("rejects near-quota member exceeding daily cumulative", async ({ page, }) => { // Thomas has 23g already — 3g more would be 26g (exceeds 25g/day) await page.goto("/distributions/new") const memberSelect = page .getByLabel(/mitglied|member/i) .or(page.locator("[data-testid='distribution-member-select']")) await memberSelect.click() await page.getByText(SEED.members.thomas.name).click() const strainSelect = page .getByLabel(/sorte|strain|charge|batch/i) .or(page.locator("[data-testid='distribution-strain-select']")) await strainSelect.click() await page.getByText(SEED.strains.northernLights.name).click() const amountInput = page .getByLabel(/menge|amount|gramm/i) .or(page.locator("input[name*='amount']")) await amountInput.fill("3") const submitBtn = page.getByRole("button", { name: /ausgeben|submit|speichern/i, }) await submitBtn.click() // Should show daily cumulative rejection await expect( page.getByText(/überschr|exceeded|limit|abgelehnt|rejected/i) ).toBeVisible({ timeout: 5000 }) }) // Requires: backend quota enforcement test("shows THC warning for under-21 members on distribution page", async ({ page, }) => { await page.goto("/distributions/new") // Select under-21 member (Jonas) const memberSelect = page .getByLabel(/mitglied|member/i) .or(page.locator("[data-testid='distribution-member-select']")) await memberSelect.click() await page.getByText(SEED.members.jonas.name).click() // Should show THC% warning/info for under-21 await expect( page.getByText(/thc.*10|unter.*21|u21|jugendschutz/i).first() ).toBeVisible({ timeout: 3000 }) }) // Requires: backend quota enforcement test("quota display shows correct remaining amount", async ({ page }) => { await page.goto("/distributions/new") // Select Thomas (near-quota member, 23g already used today) const memberSelect = page .getByLabel(/mitglied|member/i) .or(page.locator("[data-testid='distribution-member-select']")) await memberSelect.click() await page.getByText(SEED.members.thomas.name).click() // Should display remaining quota info await expect( page .getByText(/verbleibend|remaining|rest|kontingent|quota/i) .first() .or(page.locator("[data-testid*='quota']").first()) ).toBeVisible({ timeout: 3000 }) }) })