Add Catalog Studio inspection and guided import review
This commit is contained in:
1 parent
2578bf6a09
commit
26f071117b
7 files changed
+961
-44
No files matched your search
@@ -0,0 +1,67 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
compareFurniture,
|
||||
type FurnitureInspection,
|
||||
validateClassnames,
|
||||
} from "./studio-inspection";
|
||||
|
||||
const source = {
|
||||
name: "Chair",
|
||||
description: "A chair",
|
||||
revision: 2,
|
||||
type: "flooritem",
|
||||
};
|
||||
const empty: FurnitureInspection = {
|
||||
classname: "chair",
|
||||
sql: [],
|
||||
catalog: [],
|
||||
furnidata: [],
|
||||
furnidataReadable: true,
|
||||
nitro: { exists: false, bytes: 0 },
|
||||
icon: { exists: false, bytes: 0 },
|
||||
};
|
||||
describe("studio inspection", () => {
|
||||
it("rejects paths and oversized batches", () => {
|
||||
expect(validateClassnames(["../file"])).toBeNull();
|
||||
expect(validateClassnames(["a\\b"])).toBeNull();
|
||||
expect(validateClassnames(Array(501).fill("chair"))).toBeNull();
|
||||
expect(validateClassnames(["chair*0", "chair*0"])).toEqual(["chair*0"]);
|
||||
});
|
||||
it("identifies new furniture only after a successful lookup", () => {
|
||||
expect(compareFurniture(source, empty).state).toBe("new");
|
||||
expect(
|
||||
compareFurniture(source, { ...empty, furnidataReadable: false }).state,
|
||||
).toBe("unknown");
|
||||
});
|
||||
it("compares values without treating allocated IDs as conflicts", () => {
|
||||
const local = {
|
||||
...empty,
|
||||
sql: [{ id: 901, spriteId: 902, name: "Old chair", type: "s" }],
|
||||
furnidata: [
|
||||
{
|
||||
id: 902,
|
||||
name: "Old chair",
|
||||
description: "Old",
|
||||
revision: 1,
|
||||
type: "flooritem",
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(compareFurniture(source, local).state).toBe("changed");
|
||||
expect(
|
||||
compareFurniture(source, local).fields.find((f) => f.label === "Name")
|
||||
?.changed,
|
||||
).toBe(true);
|
||||
});
|
||||
it("flags duplicate SQL classnames as conflicts", () => {
|
||||
expect(
|
||||
compareFurniture(source, {
|
||||
...empty,
|
||||
sql: [
|
||||
{ id: 1, spriteId: 1, name: "Chair", type: "s" },
|
||||
{ id: 2, spriteId: 2, name: "Chair", type: "s" },
|
||||
],
|
||||
}).state,
|
||||
).toBe("conflict");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
export interface FurnitureInspection {
|
||||
classname: string;
|
||||
sql: Array<{ id: number; spriteId: number; name: string; type: string }>;
|
||||
catalog: Array<{
|
||||
id: number;
|
||||
pageId: number;
|
||||
credits: number;
|
||||
points: number;
|
||||
}>;
|
||||
furnidata: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
revision: number;
|
||||
type: string;
|
||||
}>;
|
||||
furnidataReadable: boolean;
|
||||
nitro: { exists: boolean | null; bytes: number };
|
||||
icon: { exists: boolean | null; bytes: number };
|
||||
}
|
||||
export function validateClassnames(value: unknown): string[] | null {
|
||||
if (
|
||||
!Array.isArray(value) ||
|
||||
!value.length ||
|
||||
value.length > 500 ||
|
||||
value.some(
|
||||
(v) =>
|
||||
typeof v !== "string" || v.length > 200 || !/^[a-zA-Z0-9_*-]+$/.test(v),
|
||||
)
|
||||
)
|
||||
return null;
|
||||
return [...new Set(value)];
|
||||
}
|
||||
export function compareFurniture(
|
||||
source: { name: string; description: string; revision: number; type: string },
|
||||
local: FurnitureInspection,
|
||||
) {
|
||||
const fd = local.furnidata[0],
|
||||
sql = local.sql[0];
|
||||
const values = [
|
||||
["Name", sql?.name ?? fd?.name, source.name],
|
||||
["Description", fd?.description, source.description],
|
||||
["Revision", fd?.revision, source.revision || undefined],
|
||||
[
|
||||
"Type",
|
||||
sql ? (sql.type === "i" ? "wallitem" : "flooritem") : fd?.type,
|
||||
source.type,
|
||||
],
|
||||
] as const;
|
||||
const fields = values.map(([label, before, after]) => ({
|
||||
label,
|
||||
before,
|
||||
after,
|
||||
changed:
|
||||
before !== undefined &&
|
||||
after !== undefined &&
|
||||
String(before) !== String(after),
|
||||
}));
|
||||
const state =
|
||||
local.sql.length > 1 || local.furnidata.length > 1
|
||||
? "conflict"
|
||||
: !local.furnidataReadable
|
||||
? "unknown"
|
||||
: !sql && !fd
|
||||
? "new"
|
||||
: fields.some((f) => f.changed)
|
||||
? "changed"
|
||||
: "present";
|
||||
return { state, fields } as const;
|
||||
}
|
||||
Reference in new issue
Block a user