import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import test from "node:test"; import { normalizeMapSubjectDetailProfile, normalizeMapSubjectDetailProfiles } from "./map-subject-detail-profile.mjs"; const registry = JSON.parse(await readFile(new URL("../registry/map-subject-detail-profiles.json", import.meta.url), "utf8")); test("canonical subject detail profiles are provider-neutral and valid", () => { const profiles = normalizeMapSubjectDetailProfiles(registry.profiles); assert.equal(profiles.length, 1); assert.equal(profiles[0].id, "map.subject-detail.operational.default"); assert.deepEqual(profiles[0].tabs.map((tab) => tab.id), [ "overview", "position", "telemetry", "counters", "equipment", "settings", "maintenance", "diagnostics", ]); }); test("dynamic sensor readings are fail-closed until ids are explicitly classified", () => { const profile = normalizeMapSubjectDetailProfile(registry.profiles[0]); const field = profile.tabs.find((tab) => tab.id === "telemetry").sections[0].fields[0]; assert.equal(field.format, "telemetry_readings"); assert.deepEqual(field.allowedReadingIds, []); }); test("restricted fields and reading ids cannot be introduced through MCP profiles", () => { const restrictedField = structuredClone(registry.profiles[0]); restrictedField.tabs[0].sections[0].fields[0].field = "api_key"; assert.throws(() => normalizeMapSubjectDetailProfile(restrictedField), /map_subject_detail_profile_secret_field/); const restrictedReading = structuredClone(registry.profiles[0]); restrictedReading.tabs.find((tab) => tab.id === "telemetry").sections[0].fields[0].allowedReadingIds = ["sensor.imei"]; assert.throws(() => normalizeMapSubjectDetailProfile(restrictedReading), /map_subject_detail_profile_restricted_reading_id/); }); test("restricted identifiers require an explicit restricted presentation class", () => { const profile = structuredClone(registry.profiles[0]); const field = profile.tabs[0].sections[0].fields[0]; field.field = "device_phone_primary"; field.label = "Телефон 1"; assert.throws( () => normalizeMapSubjectDetailProfile(profile), /map_subject_detail_profile_restricted_field/, ); field.dataClass = "restricted"; assert.equal( normalizeMapSubjectDetailProfile(profile).tabs[0].sections[0].fields[0].dataClass, "restricted", ); }); test("string list fields are provider-neutral attribute presentations", () => { const profile = structuredClone(registry.profiles[0]); const field = profile.tabs[0].sections[0].fields[0]; field.field = "custom_fields"; field.label = "Пользовательские поля"; field.format = "string_list"; assert.equal( normalizeMapSubjectDetailProfile(profile).tabs[0].sections[0].fields[0].format, "string_list", ); field.source = "fact"; field.field = "sourceId"; assert.throws( () => normalizeMapSubjectDetailProfile(profile), /map_subject_detail_profile_string_list_source_invalid/, ); }); test("profiles reject unknown schema fields and duplicate presentation ids", () => { const unknown = structuredClone(registry.profiles[0]); unknown.provider = "gelios"; assert.throws(() => normalizeMapSubjectDetailProfile(unknown), /invalid_map_subject_detail_profile_fields/); const duplicate = structuredClone(registry.profiles[0]); duplicate.tabs[1].sections[0].fields[0].id = duplicate.tabs[0].sections[0].fields[0].id; assert.throws(() => normalizeMapSubjectDetailProfile(duplicate), /duplicate_map_subject_detail_profile_field_id/); });