mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 14:45:24 +00:00
fix: maintenance index name, shared host read only view and badge polling
This commit is contained in:
1 parent
5ff5ae19d9
commit
3b58415f8f
16 files changed
+166
-37
No files matched your search
@@ -28,14 +28,29 @@ const input = {
|
||||
};
|
||||
|
||||
describe("host maintenance", () => {
|
||||
it("scopes reads and edits to the host owner and enforces edit permission", async () => {
|
||||
it("shows a shared host the owner's maintenance read only and enforces edit permission", async () => {
|
||||
// The mock serves every host to every user, as if alice shared host 1.
|
||||
server = await startServer({ hosts: [host(1), host(2, "bob")] });
|
||||
expect(
|
||||
(await server.request("POST", "/maintenance/1", { body: input })).status,
|
||||
).toBe(200);
|
||||
expect(
|
||||
(await server.request("GET", "/maintenance", { user: "bob" })).body,
|
||||
).toEqual([]);
|
||||
const started = await server.request("POST", "/maintenance/1", {
|
||||
body: input,
|
||||
});
|
||||
expect(started.status).toBe(200);
|
||||
expect(started.body).toMatchObject({ owned: true });
|
||||
|
||||
const listed = (
|
||||
await server.request("GET", "/maintenance", { user: "bob" })
|
||||
).body as Array<{
|
||||
hostId: number;
|
||||
state: { owned: boolean; active: unknown };
|
||||
}>;
|
||||
expect(listed).toHaveLength(1);
|
||||
expect(listed[0].hostId).toBe(1);
|
||||
expect(listed[0].state.owned).toBe(false);
|
||||
expect(listed[0].state.active).toBeTruthy();
|
||||
|
||||
const read = await server.request("GET", "/maintenance/1", { user: "bob" });
|
||||
expect(read.status).toBe(200);
|
||||
expect(read.body).toMatchObject({ owned: false });
|
||||
expect(
|
||||
(
|
||||
await server.request("POST", "/maintenance/1", {
|
||||
@@ -43,9 +58,9 @@ describe("host maintenance", () => {
|
||||
body: { action: "end" },
|
||||
})
|
||||
).status,
|
||||
).toBe(404);
|
||||
).toBe(403);
|
||||
expect(
|
||||
(await server.request("GET", "/maintenance/1", { user: "bob" })).status,
|
||||
(await server.request("GET", "/maintenance/99", { user: "bob" })).status,
|
||||
).toBe(404);
|
||||
expect(
|
||||
(await server.request("POST", "/maintenance/1x", { body: input })).status,
|
||||
|
||||
@@ -224,4 +224,36 @@ describe("adopting the automation tables", () => {
|
||||
expect(count(db, "p_automations_automations")).toBe(0);
|
||||
expect(count(db, "p_automations_run_steps")).toBe(0);
|
||||
});
|
||||
|
||||
it("creates host maintenance with one row per owner and host, removed with either", async () => {
|
||||
db = await createTestDb(pluginDir, {
|
||||
before: (sqlite) => sqlite.exec(NOTIFICATION_CHANNELS_DDL),
|
||||
});
|
||||
|
||||
const maintenanceIndexes = indexes(db, "p_automations_host_maintenance");
|
||||
expect(maintenanceIndexes).toContain(
|
||||
"idx_automation_maintenance_host_owner",
|
||||
);
|
||||
expect(maintenanceIndexes).not.toContain("maintenance_host_owner");
|
||||
|
||||
db.sqlite.exec(`
|
||||
INSERT INTO users (id, username) VALUES ('alice', 'alice');
|
||||
INSERT INTO ssh_data (id) VALUES (1);
|
||||
INSERT INTO ssh_data (id) VALUES (2);
|
||||
INSERT INTO p_automations_host_maintenance (user_id, host_id, state) VALUES ('alice', 1, '{}');
|
||||
INSERT INTO p_automations_host_maintenance (user_id, host_id, state) VALUES ('alice', 2, '{}');
|
||||
`);
|
||||
expect(() =>
|
||||
db.sqlite
|
||||
.prepare(
|
||||
"INSERT INTO p_automations_host_maintenance (user_id, host_id, state) VALUES ('alice', 1, '{}')",
|
||||
)
|
||||
.run(),
|
||||
).toThrow();
|
||||
|
||||
db.sqlite.prepare("DELETE FROM ssh_data WHERE id = 1").run();
|
||||
expect(count(db, "p_automations_host_maintenance")).toBe(1);
|
||||
db.sqlite.prepare("DELETE FROM users WHERE id = 'alice'").run();
|
||||
expect(count(db, "p_automations_host_maintenance")).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -71,6 +71,13 @@ describe("maintenance UI", () => {
|
||||
expect(view.queryByRole("button", { name: "End maintenance" })).toBeNull();
|
||||
expect(view.queryByLabelText("Reason")).toBeNull();
|
||||
});
|
||||
it("shows a shared host's maintenance read only even with edit permission", async () => {
|
||||
const view = await render(apiFor({ ...active, owned: false }));
|
||||
await waitFor(() => expect(view.getByText("OS upgrade")).toBeTruthy());
|
||||
expect(view.getByText(locales.maintenance.sharedReadOnly)).toBeTruthy();
|
||||
expect(view.queryByRole("button", { name: "End maintenance" })).toBeNull();
|
||||
expect(view.queryByLabelText("Reason")).toBeNull();
|
||||
});
|
||||
it("submits a recurring UTC schedule with its grace period", async () => {
|
||||
const api = apiFor(emptyMaintenance());
|
||||
const view = await render(api);
|
||||
|
||||
Reference in new issue
Block a user