fix(activity): runs keep their true outcome and spend, the page reaches every kept row, a closing dialog leaves the server log alone
Backend - A run's outcome write sits outside the job's own try: a database blip after a run that succeeded no longer records it as failed (the row waits for the next start's sweep). - An interrupted run keeps what it spent (the sweep sums its quota rows, as finish does). - The Scheduler's ad-hoc row says running while any backfill runs, not only the newest. - recent_runs reads each job's last runs off its index instead of ranking the whole table. - An interval change is stored with its audit row in one transaction, then rescheduled. - One API instance per database is the stated contract (.env.example, jobruns). - Turning full history on for a channel already complete records no run. - Migration 0109 round-trips interrupted and ad-hoc runs; the run words' DB CHECKs are test-held to the enums; the account filter is one helper for the four activity reads. - An audit row says how many of its request's log lines are still kept. Frontend - An "All" period reaches every row still kept (runs and admin actions outlive 90 days). - Back/Forward is followed in place by the server log: a closing dialog no longer turns Live off; a pivot still opens a fresh log. - Runs moved over from the audit log read their one-liner in the reader's language. - Filter choices or accounts that failed to load say so beside the filters, with a retry. - An old server-log link keeps its account, text and range; a remembered retired page opens its tab. - A request whose lines are gone says so instead of linking to an empty list. - The log lines and the history rows share one row frame, and the server log one list frame with the other tabs; an empty list is announced; each day's quota is readable.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { readdirSync, readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { RUN_STATUSES } from "../lib/activityUrlState";
|
||||
import { vocabKey } from "../lib/vocab";
|
||||
|
||||
// Every id the BACKEND owns and the UI names must have its name in both languages. The UI looks these
|
||||
@@ -72,6 +74,15 @@ const jobs = [
|
||||
const enumValues = (file: string) =>
|
||||
[...backend(file).matchAll(/^\s+[A-Z_]+ = "(\w+)"$/gm)].map((m) => m[1]!);
|
||||
const auditActions = enumValues("audit.py");
|
||||
// A background run's words (backend/app/runstatus.py): its status and what started it.
|
||||
const runEnum = (name: string) =>
|
||||
[
|
||||
...(
|
||||
backend("runstatus.py").split(`class ${name}(StrEnum):`)[1]?.split("\nclass ")[0] ?? ""
|
||||
).matchAll(/^\s+[A-Z_]+ = "(\w+)"$/gm),
|
||||
].map((m) => m[1]!);
|
||||
const runStatuses = runEnum("RunStatus");
|
||||
const runTriggers = runEnum("RunTrigger");
|
||||
const quotaActions = enumValues("quota.py");
|
||||
// The admin actions' refusal reasons → `errors.admin.<reason>`. The codes are read from
|
||||
// `localizeDetail`'s own ADMIN_REFUSALS, so a code added there is scanned here too; a detail dict
|
||||
@@ -260,6 +271,8 @@ describe("backend ids have their names in both languages", () => {
|
||||
expect(jobs).toEqual(expect.arrayContaining(["rss_poll", "audit_gc"]));
|
||||
expect(auditActions).toEqual(expect.arrayContaining(["config_set", "audit_clear"]));
|
||||
expect(quotaActions.length).toBeGreaterThan(0);
|
||||
expect(runStatuses).toEqual(expect.arrayContaining(["running", "interrupted"]));
|
||||
expect(runTriggers).toEqual(expect.arrayContaining(["scheduled", "ad_hoc"]));
|
||||
expect(adminCodes).toEqual(expect.arrayContaining(["activate", "resend"]));
|
||||
expect(failDetails).toEqual(expect.arrayContaining(["facebook.login_wall"]));
|
||||
expect(goneWhats).toEqual(expect.arrayContaining(["playlist", "download", "job"]));
|
||||
@@ -288,10 +301,23 @@ describe("backend ids have their names in both languages", () => {
|
||||
expect(adminCodes.filter((c) => !adminRefusals.some((m) => m[1] === c))).toEqual([]);
|
||||
});
|
||||
|
||||
it("the run list's status chips are the server's run statuses", () => {
|
||||
expect([...RUN_STATUSES].sort()).toEqual([...runStatuses].sort());
|
||||
});
|
||||
|
||||
for (const lang of ["en", "hu"]) {
|
||||
const keys = keysOf(lang);
|
||||
const missing = (wanted: string[]) => wanted.filter((k) => !keys.has(k));
|
||||
|
||||
it(`${lang}: every run status and trigger`, () => {
|
||||
expect(
|
||||
missing([
|
||||
...runStatuses.flatMap((s) => [`activity.runs.status.${s}`, `scheduler.dot.${s}`]),
|
||||
...runTriggers.map((s) => `scheduler.trigger.${s}`),
|
||||
]),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it(`${lang}: every config key and group`, () => {
|
||||
expect(
|
||||
missing([
|
||||
|
||||
Reference in New Issue
Block a user