Files
kandev/apps/web/lib/state/slices/system/system-slice.test.ts
T
2026-09-06 16:09:03 +00:00

254 lines
7.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { createSystemSlice, defaultSystemState } from "./system-slice";
import type { SystemSlice } from "./types";
import type {
SystemInfo,
DiskUsageResponse,
DatabaseStats,
SnapshotInfo,
UpdatesResponse,
SystemJob,
StorageOverviewResponse,
} from "@/lib/types/system";
const TS = "2026-05-18T00:00:00Z";
function makeStore() {
return create<SystemSlice>()(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
immer((...a) => ({ ...(createSystemSlice as any)(...a) })),
);
}
const INFO: SystemInfo = {
version: "1.2.3",
commit: "abc1234",
build_time: "2026-01-01T00:00:00Z",
go_version: "go1.24",
os: "darwin",
arch: "arm64",
boot_id: "boot-1",
started_at: "2026-01-01T00:00:00Z",
};
const DISK_USAGE: DiskUsageResponse = {
data: {
data_dir: 100,
worktrees: 200,
repos: 300,
sessions: 400,
tasks: 500,
quick_chat: 600,
backups: 700,
total: 2800,
warnings: [],
computed_at: TS,
},
computing: false,
home_dir: "/data/kandev",
};
const DB_STATS: DatabaseStats = {
driver: "sqlite",
path: "/data/kandev.db",
backup_directory: "/data/backups",
size_bytes: 12345,
wal_size_bytes: 678,
schema_version: "1.0.0",
last_backup_at: "2026-05-17T00:00:00Z",
};
const SNAPSHOT: SnapshotInfo = {
name: "manual-1.db",
size_bytes: 1024,
mtime: "2026-05-17T00:00:00Z",
kind: "manual",
};
const UPDATES: UpdatesResponse = {
current: "1.2.3",
latest: "1.2.4",
latest_url: "https://github.com/kdlbs/kandev/releases/1.2.4",
latest_checked_at: TS,
update_available: true,
channel: "stable",
channel_editable: true,
channel_unsupported_reason: "",
};
const JOB: SystemJob = {
id: "job-1",
kind: "vacuum",
state: "running",
started_at: TS,
};
const ANALYSIS = {
generation: 1,
state: "ready",
started_at: TS,
completed_at: TS,
duration_ms: 10,
cache_ttl_seconds: 900,
refresh_due_at: "2026-05-18T00:15:00Z",
stale: false,
error: null,
progress: { completed_sources: 5, total_sources: 5, sources: {} },
partial_summary: null,
} as const;
describe("system storage slice", () => {
it("stores storage overview, runs, and quarantine state", () => {
const store = makeStore();
const overview = {
settings: {
enabled: false,
check_interval_hours: 24,
idle_for_minutes: 10,
orphan_grace_hours: 168,
quarantine_retention_hours: 168,
workspaces: { enabled: true, dependency_cleanup_enabled: false },
kandev_containers: { enabled: true },
go_cache: { enabled: false, max_bytes: 16106127360, adopted_path: "" },
docker: {
dedicated_daemon_acknowledged: false,
build_cache_enabled: false,
build_cache_keep_bytes: 10737418240,
build_cache_unused_hours: 168,
unused_images_enabled: false,
unused_images_hours: 168,
},
},
capabilities: {
managed_go_cache_path: "/data/cache/go-build",
go_cache_adoption_available: true,
temporary_artifacts_available: false,
docker_available: false,
docker_host: "",
host_global_docker_cleanup_allowed: false,
},
summary: {
workspaces: { active_bytes: 1, candidate_bytes: 2 },
go_cache: { path: "/data/cache/go-build", size_bytes: 3, owned: true, enabled: false },
quarantine: { count: 0, size_bytes: 0 },
temporary_artifacts: {
available: false,
warning: "temporary artifact registry unavailable",
},
docker: {
available: false,
build_cache_bytes: 0,
unused_image_bytes: 0,
managed_container_count: 0,
managed_container_bytes: 0,
},
},
analysis: ANALYSIS,
analyzed_at: "2026-07-23T12:00:00Z",
last_run: null,
} satisfies StorageOverviewResponse;
const policy = { settings: overview.settings, capabilities: overview.capabilities };
store.getState().setSystemStoragePolicy(policy);
store.getState().setSystemStorageOverview(overview);
store.getState().setSystemStorageRuns([]);
store.getState().setSystemStorageQuarantine([]);
expect(store.getState().system.storage).toEqual({
policy,
overview,
analysisRevision: 0,
disk: null,
runs: [],
quarantine: [],
});
});
});
describe("system slice", () => {
it("starts with empty defaults", () => {
const store = makeStore();
const s = store.getState();
expect(s.system).toEqual(defaultSystemState.system);
expect(s.system.info).toBeNull();
expect(s.system.diskUsage).toBeNull();
expect(s.system.database).toBeNull();
expect(s.system.backups).toEqual({ items: [], loaded: false });
expect(s.system.updates).toBeNull();
expect(s.system.jobs).toEqual({});
});
it("setSystemInfo stores the payload", () => {
const store = makeStore();
store.getState().setSystemInfo(INFO);
expect(store.getState().system.info).toEqual(INFO);
});
it("setSystemDiskUsage replaces the cached response", () => {
const store = makeStore();
store.getState().setSystemDiskUsage(DISK_USAGE);
expect(store.getState().system.diskUsage).toEqual(DISK_USAGE);
const computing: DiskUsageResponse = { data: null, computing: true, home_dir: "/data/kandev" };
store.getState().setSystemDiskUsage(computing);
expect(store.getState().system.diskUsage).toEqual(computing);
});
it("setSystemDatabase stores the stats", () => {
const store = makeStore();
store.getState().setSystemDatabase(DB_STATS);
expect(store.getState().system.database).toEqual(DB_STATS);
});
it("setSystemBackups marks the list as loaded", () => {
const store = makeStore();
store.getState().setSystemBackups([SNAPSHOT]);
expect(store.getState().system.backups).toEqual({ items: [SNAPSHOT], loaded: true });
// Empty list also flips loaded to true.
store.getState().setSystemBackups([]);
expect(store.getState().system.backups).toEqual({ items: [], loaded: true });
});
it("setSystemUpdates stores the response", () => {
const store = makeStore();
store.getState().setSystemUpdates(UPDATES);
expect(store.getState().system.updates).toEqual(UPDATES);
});
it("upsertSystemJob inserts and updates by id", () => {
const store = makeStore();
store.getState().upsertSystemJob(JOB);
expect(store.getState().system.jobs["job-1"]).toEqual(JOB);
const finished: SystemJob = { ...JOB, state: "succeeded", ended_at: "2026-05-18T00:01:00Z" };
store.getState().upsertSystemJob(finished);
expect(store.getState().system.jobs["job-1"]).toEqual(finished);
// Same id, still one entry.
expect(Object.keys(store.getState().system.jobs)).toEqual(["job-1"]);
});
it("clearSystemJob removes the entry", () => {
const store = makeStore();
store.getState().upsertSystemJob(JOB);
store.getState().upsertSystemJob({ ...JOB, id: "job-2" });
store.getState().clearSystemJob("job-1");
expect(store.getState().system.jobs["job-1"]).toBeUndefined();
expect(store.getState().system.jobs["job-2"]).toBeDefined();
});
it("clearSystemJob is a no-op for missing ids", () => {
const store = makeStore();
store.getState().clearSystemJob("does-not-exist");
expect(store.getState().system.jobs).toEqual({});
});
it("advances the storage analysis revision for live updates", () => {
const store = makeStore();
expect(store.getState().system.storage.analysisRevision).toBe(0);
store.getState().bumpSystemStorageAnalysisRevision();
store.getState().bumpSystemStorageAnalysisRevision();
expect(store.getState().system.storage.analysisRevision).toBe(2);
});
});