Address CodeRabbit provider feedback

This commit is contained in:
ajnart committed 2026-08-11 22:03:21 +02:00
1 parent ea9075bde5
commit abaf845e94
19 files changed
+293 -64

No files matched your search

+12 -7
View File
@@ -291,11 +291,12 @@ export class WorkshopBackend {
public get currentUser(): WorkshopUser | null {
const record = this.pocketBase.authStore.record;
if (!record) return null;
return workshopUserSchema.parse({
const parsed = workshopUserSchema.safeParse({
id: record.id,
name: record.name || "",
isAdmin: record.isAdmin === true,
});
return parsed.success ? parsed.data : null;
}
public get authToken(): string | null {
@@ -326,7 +327,9 @@ export class WorkshopBackend {
provider: "github",
});
this.pocketBase.authStore.save(auth.token, auth.record);
return this.currentUser;
const user = this.currentUser;
if (!user) throw new Error("The Workshop returned an invalid user session.");
return user;
} catch (error) {
throw workshopError(error, "GitHub sign-in failed");
}
@@ -351,11 +354,13 @@ export class WorkshopBackend {
public async listAssistantActivity(limit = 10, signal?: AbortSignal): Promise<WorkshopAssistantActivity[]> {
try {
const result = await this.pocketBase.collection("assistant_activity").getList(1, Math.min(Math.max(limit, 1), 50), {
sort: "-created",
signal: requestSignal(signal),
requestKey: null,
});
const result = await this.pocketBase
.collection("assistant_activity")
.getList(1, Math.min(Math.max(limit, 1), 50), {
sort: "-created",
signal: requestSignal(signal),
requestKey: null,
});
return result.items.flatMap((item) => {
const parsed = workshopAssistantActivitySchema.safeParse(item);
return parsed.success ? [parsed.data] : [];
+26 -6
View File
@@ -15,16 +15,21 @@ vi.mock("pocketbase", () => {
public readonly authStore = {
token: "",
record: null as Record<string, unknown> | null,
isValid: false,
get isValid() {
try {
const payload = JSON.parse(atob(this.token.split(".")[1] ?? "")) as { exp?: unknown };
return typeof payload.exp === "number" && payload.exp > Date.now() / 1000;
} catch {
return false;
}
},
save: (token: string, record: Record<string, unknown>) => {
this.authStore.token = token;
this.authStore.record = record;
this.authStore.isValid = true;
},
clear: () => {
this.authStore.token = "";
this.authStore.record = null;
this.authStore.isValid = false;
},
};
@@ -63,6 +68,8 @@ import { WorkshopBackend } from "./backend";
import type { WorkshopSubmissionRecord } from "./backend";
import * as workshopClient from "./client";
const validWorkshopToken = "header.eyJleHAiOjQxMDI0NDQ4MDB9.signature";
describe("WorkshopBackend", () => {
test("resolves the advertised client module", () => {
expect(workshopClient.WorkshopBackend).toBe(WorkshopBackend);
@@ -108,7 +115,7 @@ describe("WorkshopBackend", () => {
test("reuses a valid Workshop session for the Homarr provider allowance", async () => {
const client = new WorkshopBackend("https://workshop.example.com");
expect(client.authToken).toBeNull();
client.pocketBase.authStore.save("workshop-token", { id: "author-id", name: "octocat" } as never);
client.pocketBase.authStore.save(validWorkshopToken, { id: "author-id", name: "octocat" } as never);
mocks.send.mockResolvedValue({
limit: 50,
used: 7,
@@ -117,13 +124,26 @@ describe("WorkshopBackend", () => {
});
await expect(client.getAssistantUsage()).resolves.toMatchObject({ limit: 50, remaining: 43 });
expect(client.authToken).toBe("workshop-token");
expect(client.authToken).toBe(validWorkshopToken);
expect(mocks.send).toHaveBeenCalledWith(
"/api/ai/usage",
expect.objectContaining({ method: "GET", requestKey: null, signal: expect.any(AbortSignal) }),
);
});
test("fails closed for expired tokens and malformed Workshop user records", () => {
const client = new WorkshopBackend("https://workshop.example.com");
client.pocketBase.authStore.save("header.eyJleHAiOjF9.signature", {
id: "author-id",
name: "octocat",
} as never);
expect(client.authToken).toBeNull();
client.pocketBase.authStore.save(validWorkshopToken, { id: 42 } as never);
expect(client.currentUser).toBeNull();
expect(client.authToken).toBeNull();
});
test("loads only valid anonymous provider activity records", async () => {
mocks.getList.mockResolvedValue({
items: [
@@ -155,7 +175,7 @@ describe("WorkshopBackend", () => {
test("sends the exported schema while leaving initial revision ownership to PocketBase", async () => {
const client = new WorkshopBackend("https://workshop.example.com");
client.pocketBase.authStore.save("token", { id: "author-id" } as never);
client.pocketBase.authStore.save(validWorkshopToken, { id: "author-id" } as never);
mocks.create.mockResolvedValue({ id: "submission-id" });
vi.spyOn(client, "get").mockResolvedValue(listingRecord() as never);
+1 -1
View File
@@ -106,7 +106,7 @@ export const workshopAssistantUsageSchema = z.object({
limit: z.number().int().nonnegative(),
used: z.number().int().nonnegative(),
remaining: z.number().int().nonnegative(),
resetsAt: z.string().datetime({ offset: true }),
resetsAt: z.iso.datetime({ offset: true }),
});
export type WorkshopAssistantUsage = z.infer<typeof workshopAssistantUsageSchema>;