add legislation API client for frontend

This commit is contained in:
2026-03-10 16:53:21 +01:00
parent a4998190c3
commit 3baf126a55
2 changed files with 117 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
import { beforeEach, describe, expect, it, vi } from "vitest"
const mockFetch = vi.fn()
vi.stubGlobal("fetch", mockFetch)
const { fetchLegislation, castVote, fetchUserVote } = await import(
"./legislation-api"
)
describe("legislation-api", () => {
beforeEach(() => {
mockFetch.mockReset()
})
it("fetches legislation detail", async () => {
mockFetch.mockResolvedValueOnce(
new Response(
JSON.stringify({
id: 1,
dipVorgangsId: 100,
title: "Test Gesetz",
abstract: "Summary",
fullText: null,
summary: null,
fetchedAt: "2026-03-10T00:00:00Z",
}),
),
)
const result = await fetchLegislation(1)
expect(result.title).toBe("Test Gesetz")
})
it("casts a vote", async () => {
mockFetch.mockResolvedValueOnce(
new Response(JSON.stringify({ ok: true }), { status: 201 }),
)
await castVote(1, "device-123", "ja")
expect(mockFetch).toHaveBeenCalledOnce()
const [url, opts] = mockFetch.mock.calls[0]
expect(url).toContain("/legislation/1/vote")
expect(opts.method).toBe("POST")
})
it("fetches existing user vote", async () => {
mockFetch.mockResolvedValueOnce(
new Response(
JSON.stringify({
legislationId: 1,
vote: "ja",
votedAt: "2026-03-10T00:00:00Z",
}),
),
)
const result = await fetchUserVote(1, "device-123")
expect(result).not.toBeNull()
expect(result?.vote).toBe("ja")
})
it("returns null when no user vote exists (404)", async () => {
mockFetch.mockResolvedValueOnce(
new Response(JSON.stringify({ error: "no vote found" }), {
status: 404,
}),
)
const result = await fetchUserVote(1, "device-123")
expect(result).toBeNull()
})
})

View File

@@ -0,0 +1,45 @@
import { BACKEND_URL } from "@/shared/lib/constants"
import type {
LegislationDetail,
UserVoteChoice,
UserVoteRecord,
} from "../../../../shared/legislation-types"
export async function fetchLegislation(id: number): Promise<LegislationDetail> {
const res = await fetch(`${BACKEND_URL}/legislation/${id}`)
if (!res.ok) throw new Error(`Failed to fetch legislation ${id}`)
return res.json()
}
export async function fetchLegislationText(id: number): Promise<string | null> {
const res = await fetch(`${BACKEND_URL}/legislation/${id}/text`)
if (!res.ok) return null
const data = await res.json()
return data.text ?? null
}
export async function castVote(
legislationId: number,
deviceId: string,
vote: UserVoteChoice,
): Promise<void> {
const res = await fetch(`${BACKEND_URL}/legislation/${legislationId}/vote`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ deviceId, vote }),
})
if (!res.ok)
throw new Error(`Failed to cast vote for legislation ${legislationId}`)
}
export async function fetchUserVote(
legislationId: number,
deviceId: string,
): Promise<UserVoteRecord | null> {
const res = await fetch(
`${BACKEND_URL}/legislation/${legislationId}/vote/${deviceId}`,
)
if (res.status === 404) return null
if (!res.ok) throw new Error("Failed to fetch vote")
return res.json()
}