276 lines
12 KiB
JavaScript
276 lines
12 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import {
|
|
EXTERNAL_PROVIDER_CONTRACT_VERSION,
|
|
FOUNDRY_BINDING_UPSERT_SCHEMA_VERSION,
|
|
assertValid,
|
|
validateCollectionProfile,
|
|
validateConnectionProfile,
|
|
validateDataProduct,
|
|
validateFoundryBinding,
|
|
validateFoundryBindingUpsert,
|
|
validateIntakeBatch,
|
|
validateProviderManifest,
|
|
} from "../src/index.mjs";
|
|
import { geliosPositionsCurrentExample } from "../examples/gelios-positions-current.v1.mjs";
|
|
|
|
assert.equal(validateProviderManifest(geliosPositionsCurrentExample.providerManifest).ok, true);
|
|
assert.equal(validateConnectionProfile(geliosPositionsCurrentExample.connection).ok, true);
|
|
assert.equal(validateConnectionProfile({
|
|
...geliosPositionsCurrentExample.connection,
|
|
credentialRef: { ...geliosPositionsCurrentExample.connection.credentialRef, owner: "engine" },
|
|
}).errors.includes("credentialRef.owner_must_be_ndc_l2_credentials"), true);
|
|
assert.equal(validateCollectionProfile(geliosPositionsCurrentExample.collectionProfile).ok, true);
|
|
assert.equal(validateDataProduct(geliosPositionsCurrentExample.dataProduct).ok, true);
|
|
assert.equal(validateFoundryBinding(geliosPositionsCurrentExample.foundryBinding).ok, true);
|
|
|
|
const foundryBindingUpsert = {
|
|
schemaVersion: FOUNDRY_BINDING_UPSERT_SCHEMA_VERSION,
|
|
applicationId: "11111111-1111-4111-8111-111111111111",
|
|
pageId: "map",
|
|
idempotencyKey: "foundry-binding-0123456789abcdef0123456789abcdef",
|
|
binding: {
|
|
id: "fleet-live-points",
|
|
dataProductId: "fleet.positions.current.v1",
|
|
slotId: "points",
|
|
semanticTypes: ["map.moving_object"],
|
|
fieldProjection: ["name", "speed", "course"],
|
|
},
|
|
};
|
|
assert.equal(validateFoundryBindingUpsert(foundryBindingUpsert).ok, true);
|
|
assert.equal(validateFoundryBindingUpsert({
|
|
...foundryBindingUpsert,
|
|
binding: { ...foundryBindingUpsert.binding, semanticTypes: ["map.moving_object", "map.moving_object"] },
|
|
}).errors.includes("binding.semanticTypes_must_not_contain_duplicates"), true);
|
|
assert.equal(validateFoundryBindingUpsert({
|
|
...foundryBindingUpsert,
|
|
binding: { ...foundryBindingUpsert.binding, accessToken: "forbidden" },
|
|
}).errors.includes("binding.accessToken_not_allowed"), true);
|
|
assert.equal(validateFoundryBindingUpsert({
|
|
...foundryBindingUpsert,
|
|
binding: { ...foundryBindingUpsert.binding, fieldProjection: ["ndc_edprb_forbidden-reader-token"] },
|
|
}).errors.includes("binding_must_not_contain_secret_material"), true);
|
|
|
|
const attributesWithSerializedSize = (size) => ({
|
|
blob: "x".repeat(size - Buffer.byteLength(JSON.stringify({ blob: "" }))),
|
|
});
|
|
|
|
const neutralIntakeBatch = {
|
|
schemaVersion: EXTERNAL_PROVIDER_CONTRACT_VERSION,
|
|
source: {
|
|
providerId: "example-provider",
|
|
tenantId: "sample-tenant",
|
|
connectionId: "sample-connection",
|
|
},
|
|
contract: {
|
|
dataProductId: "fleet.positions.current.v1",
|
|
ontologyRevision: "ontology.example-fleet.v1",
|
|
version: "1.0.0",
|
|
},
|
|
batch: {
|
|
runId: "run-20260714-001",
|
|
sequence: 0,
|
|
idempotencyKey: "run-20260714-001.batch-0",
|
|
receivedAt: "2026-07-14T18:00:00.000Z",
|
|
},
|
|
raw: {
|
|
contentType: "application/json",
|
|
hash: "sha256:example",
|
|
ref: "restricted://raw/run-20260714-001",
|
|
retentionDays: 14,
|
|
},
|
|
facts: [{
|
|
sourceId: "source-object-42",
|
|
semanticType: "map.moving_object",
|
|
observedAt: "2026-07-14T17:59:58.000Z",
|
|
geometry: { type: "Point", coordinates: [37.6173, 55.7558] },
|
|
attributes: { status: "active" },
|
|
}],
|
|
};
|
|
|
|
assert.equal(validateIntakeBatch(neutralIntakeBatch).ok, true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
raw: { contentType: "application/json", payload: { safe: "fixture" } },
|
|
}).errors.includes("raw.inline_payload_not_supported"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
raw: { contentType: "application/json", payload: "unsafe-unstructured-inline-raw" },
|
|
}).errors.includes("raw.inline_payload_not_supported"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
raw: { contentType: "application/json", payload: { providerAccessToken: "must-never-be-stored" } },
|
|
}).errors.includes("intake_must_not_contain_secret_material"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], attributes: { authorization: "Bearer must-never-be-stored" } }],
|
|
}).errors.includes("intake_must_not_contain_secret_material"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], attributes: { metadata: "ndc_edpwb_abcdefghijklmnopqrstuvwxyz0123456789ABCDE" } }],
|
|
}).errors.includes("intake_must_not_contain_secret_material"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], attributes: { metadata: "ndc_edprb_abcdefghijklmnopqrstuvwxyz0123456789ABCDE" } }],
|
|
}).errors.includes("intake_must_not_contain_secret_material"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], attributes: { metadata: `ndc_edppr_${"P".repeat(43)}` } }],
|
|
}).errors.includes("intake_must_not_contain_secret_material"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
raw: { contentType: "application/json", hash: "prefix ndc_edpwb_abcdefghijklmnopqrstuvwxyz0123456789ABCDE suffix", ref: "restricted://raw/fixture" },
|
|
}).errors.includes("intake_must_not_contain_secret_material"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], attributes: { metadata: "Bearer opaque-secret-material" } }],
|
|
}).errors.includes("intake_must_not_contain_secret_material"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
raw: { contentType: "application/json", hash: "sha256:fixture", ref: "restricted://raw?access_token=must-never-be-stored" },
|
|
}).errors.includes("raw.ref_must_not_contain_secret_material"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], sourceId: "" }],
|
|
}).errors.includes("facts[0].sourceId_invalid"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
batch: { ...neutralIntakeBatch.batch, sequence: 2_147_483_647 },
|
|
}).ok, true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
batch: { ...neutralIntakeBatch.batch, sequence: 2_147_483_648 },
|
|
}).errors.includes("batch.sequence_must_be_integer_0_to_2147483647"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], attributes: attributesWithSerializedSize(64 * 1024) }],
|
|
}).ok, true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], attributes: attributesWithSerializedSize((64 * 1024) + 1) }],
|
|
}).errors.includes("facts[0].attributes_size_exceeded"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], geometry: { type: "Point", coordinates: [180.0001, 55.7558] } }],
|
|
}).errors.includes("facts[0].geometry.longitude_out_of_range"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
facts: [{ ...neutralIntakeBatch.facts[0], geometry: { type: "Point", coordinates: [37.6173, -90.0001] } }],
|
|
}).errors.includes("facts[0].geometry.latitude_out_of_range"), true);
|
|
assert.equal(validateIntakeBatch({
|
|
...neutralIntakeBatch,
|
|
debug: true,
|
|
}).errors.includes("intakeBatch.debug_not_allowed"), true);
|
|
|
|
assert.equal(validateConnectionProfile({
|
|
...geliosPositionsCurrentExample.connection,
|
|
apiToken: "must-never-be-here",
|
|
}).ok, false);
|
|
assert.equal(validateConnectionProfile({
|
|
...geliosPositionsCurrentExample.connection,
|
|
credentialRef: {
|
|
...geliosPositionsCurrentExample.connection.credentialRef,
|
|
reference: "ndc_edpwb_not-allowed-even-in-a-non-secret-field",
|
|
},
|
|
}).errors.includes("profile_must_not_contain_secret_material"), true);
|
|
assert.equal(validateConnectionProfile({
|
|
...geliosPositionsCurrentExample.connection,
|
|
credentialRef: {
|
|
...geliosPositionsCurrentExample.connection.credentialRef,
|
|
reference: "opaque-reference?access_token=must-not-cross-the-boundary",
|
|
},
|
|
}).errors.includes("profile_must_not_contain_secret_material"), true);
|
|
assert.equal(validateConnectionProfile({
|
|
...geliosPositionsCurrentExample.connection,
|
|
credentialRef: { ...geliosPositionsCurrentExample.connection.credentialRef, namespace: "unexpected" },
|
|
}).errors.includes("credentialRef.namespace_not_allowed"), true);
|
|
assert.equal(validateConnectionProfile({
|
|
...geliosPositionsCurrentExample.connection,
|
|
scope: { ...geliosPositionsCurrentExample.connection.scope, providerSelector: "unexpected" },
|
|
}).errors.includes("scope.providerSelector_not_allowed"), true);
|
|
assert.equal(validateCollectionProfile({
|
|
...geliosPositionsCurrentExample.collectionProfile,
|
|
mode: "manual",
|
|
}).errors.includes("manual_profile_must_not_define_intervalMs"), true);
|
|
assert.equal(validateCollectionProfile({
|
|
...geliosPositionsCurrentExample.collectionProfile,
|
|
schedule: { intervalMs: 3000, jitterMs: 100 },
|
|
}).errors.includes("schedule.jitterMs_not_allowed"), true);
|
|
assert.equal(validateCollectionProfile({
|
|
...geliosPositionsCurrentExample.collectionProfile,
|
|
capabilityIds: ["ndc_edprb_forbidden-reader-token"],
|
|
}).errors.includes("collectionProfile_must_not_contain_secret_material"), true);
|
|
assert.equal(validateFoundryBinding({
|
|
...geliosPositionsCurrentExample.foundryBinding,
|
|
providerId: "gelios",
|
|
}).errors.includes("binding_must_reference_data_product_not_provider_transport"), true);
|
|
assert.equal(validateFoundryBinding({
|
|
...geliosPositionsCurrentExample.foundryBinding,
|
|
applicationId: undefined,
|
|
}).errors.includes("applicationId_invalid"), true);
|
|
assert.equal(validateFoundryBinding({
|
|
...geliosPositionsCurrentExample.foundryBinding,
|
|
pageId: undefined,
|
|
}).errors.includes("pageId_invalid"), true);
|
|
assert.equal(validateFoundryBinding({
|
|
...geliosPositionsCurrentExample.foundryBinding,
|
|
templateId: undefined,
|
|
}).ok, true);
|
|
assert.equal(validateFoundryBinding({
|
|
...geliosPositionsCurrentExample.foundryBinding,
|
|
rendererOptions: {},
|
|
}).errors.includes("foundryBinding.rendererOptions_not_allowed"), true);
|
|
assert.equal(validateFoundryBinding({
|
|
...geliosPositionsCurrentExample.foundryBinding,
|
|
semanticType: "ndc_edprb_forbidden-reader-token",
|
|
}).errors.includes("binding_must_not_contain_secret_material"), true);
|
|
|
|
assert.equal(validateProviderManifest({
|
|
...geliosPositionsCurrentExample.providerManifest,
|
|
tenantId: "must-not-live-in-provider-manifest",
|
|
}).errors.includes("manifest_must_not_contain_connection_runtime_state"), true);
|
|
assert.equal(validateProviderManifest({
|
|
...geliosPositionsCurrentExample.providerManifest,
|
|
apiToken: "must-never-be-here",
|
|
}).errors.includes("manifest_must_not_contain_secret_material"), true);
|
|
assert.equal(validateProviderManifest({
|
|
...geliosPositionsCurrentExample.providerManifest,
|
|
endpoint: "https://must-live-in-l2-template.invalid",
|
|
}).errors.includes("manifest_must_not_contain_provider_transport"), true);
|
|
assert.equal(validateProviderManifest({
|
|
...geliosPositionsCurrentExample.providerManifest,
|
|
capabilities: [{ id: "gelios.units.current.read", classification: "not-a-class" }],
|
|
}).errors.includes("capabilities[0].classification_invalid"), true);
|
|
assert.equal(validateProviderManifest({
|
|
...geliosPositionsCurrentExample.providerManifest,
|
|
metadata: {},
|
|
}).errors.includes("providerManifest.metadata_not_allowed"), true);
|
|
assert.equal(validateProviderManifest({
|
|
...geliosPositionsCurrentExample.providerManifest,
|
|
ontology: { ...geliosPositionsCurrentExample.providerManifest.ontology, transport: "unexpected" },
|
|
}).errors.includes("ontology.transport_not_allowed"), true);
|
|
assert.equal(validateProviderManifest({
|
|
...geliosPositionsCurrentExample.providerManifest,
|
|
capabilities: [{ id: "gelios.units.current.read", classification: "read", endpoint: "/units" }],
|
|
}).errors.includes("capabilities[0].endpoint_not_allowed"), true);
|
|
assert.equal(validateProviderManifest({
|
|
...geliosPositionsCurrentExample.providerManifest,
|
|
dataProductIds: ["ndc_edpwb_forbidden-writer-token"],
|
|
}).errors.includes("manifest_must_not_contain_secret_material"), true);
|
|
|
|
assert.equal(validateDataProduct({
|
|
...geliosPositionsCurrentExample.dataProduct,
|
|
delivery: { mode: "snapshot+patch", transport: "sse" },
|
|
}).errors.includes("delivery.transport_not_allowed"), true);
|
|
|
|
assert.throws(() => assertValid(validateDataProduct, {
|
|
schemaVersion: EXTERNAL_PROVIDER_CONTRACT_VERSION,
|
|
id: "fleet.positions.current.v1",
|
|
version: "1.0.0",
|
|
delivery: { mode: "snapshot" },
|
|
semanticTypes: [],
|
|
fields: [],
|
|
access: { audience: "public" },
|
|
}));
|
|
|
|
console.log("external-provider-contract: ok");
|