NODEDC_PLATFORM/packages/external-provider-contract/test/provider-package.test.mjs

748 lines
35 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import {
L2_CONNECTION_INSTANCE_SCHEMA_VERSION,
instantiateL2Connection,
validateDataProductPublish,
validateProviderPackage,
} from "../src/index.mjs";
import {
GELIOS_POSITIONS_DATA_PRODUCT_ID,
GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
GELIOS_POSITIONS_ONTOLOGY_REVISION,
GELIOS_UNIT_SOURCE_ID_PREFIX,
geliosProviderPackageV1,
geliosUnitsCurrentFixtureV1,
} from "../providers/gelios/v1/index.mjs";
import { geliosProviderPackageV2 } from "../providers/gelios/v2/index.mjs";
import { geliosProviderPackageV3 } from "../providers/gelios/v3/index.mjs";
import { geliosProviderPackageV4 } from "../providers/gelios/v4/index.mjs";
import { geliosProviderPackageV5 } from "../providers/gelios/v5/index.mjs";
import { geliosProviderPackageV6 } from "../providers/gelios/v6/index.mjs";
import { geliosProviderPackageV7 } from "../providers/gelios/v7/index.mjs";
import { geliosProviderPackageV8 } from "../providers/gelios/v8/index.mjs";
import { geliosProviderPackageV9 } from "../providers/gelios/v9/index.mjs";
import { normalizeDataProductDefinition } from "../../../services/external-data-plane/src/data-product-policy.mjs";
const expectedFields = [
"course_degrees",
"display_name",
"elevation_meters",
"geometry",
"hdop",
"horizontal_accuracy_meters",
"object_kind",
"operational_status",
"position_source",
"position_valid",
"quality_flags",
"satellite_count",
"speed_kph",
];
assert.deepEqual(validateProviderPackage(geliosProviderPackageV1), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV2), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV3), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV4), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV5), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV6), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV7), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV8), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV9), { ok: true, errors: [] });
assert.equal(geliosProviderPackageV9.id, "gelios.provider.v9");
assert.equal(geliosProviderPackageV9.version, "9.0.0");
assert.equal(geliosProviderPackageV8.id, "gelios.provider.v8");
const profileProduct = geliosProviderPackageV8.dataProducts.find((product) => product.id === "fleet.units.profile.current.v1");
const registeredProfileProduct = JSON.parse(await readFile(new URL(
"../../../services/external-data-plane/definitions/fleet.units.profile.current.v1.json",
import.meta.url,
), "utf8"));
assert.deepEqual(profileProduct, registeredProfileProduct);
assert.deepEqual(profileProduct.semanticTypes, ["map.moving_object"]);
assert.equal(profileProduct.history.mode, "none");
const profileCollection = geliosProviderPackageV8.collectionProfiles.find((profile) => profile.id === "gelios.units.profile.cold.v1");
assert.equal(profileCollection.schedule.intervalMs, 6 * 60 * 60 * 1000);
assert.deepEqual(profileCollection.capabilityIds, ["gelios.units.current.read"]);
const profileMapping = geliosProviderPackageV8.mappingContracts.find((mapping) => mapping.id === "gelios.units.to.fleet.units.profile.current.v1");
assert.equal(profileMapping.fact.sourceId.prefix, "gelios-unit-");
assert.equal(profileMapping.fact.attributes.hardware_type_name.paths[0], "hwType.name");
assert.equal(profileMapping.fact.attributes.trip_detection_type.paths[0], "tripParams.tripDetectionType.name");
assert.equal(profileMapping.fact.attributes.use_odometer.coerce, "boolean");
const telemetryProduct = geliosProviderPackageV7.dataProducts.find((product) => product.id === "fleet.positions.current.v5");
const registeredTelemetryProduct = JSON.parse(await readFile(new URL(
"../../../services/external-data-plane/definitions/fleet.positions.current.v5.json",
import.meta.url,
), "utf8"));
assert.deepEqual(telemetryProduct, registeredTelemetryProduct);
assert.equal(telemetryProduct.fieldContracts.sensor_readings.type, "telemetry_readings");
const telemetryCapability = geliosProviderPackageV7.capabilities.find((capability) => capability.id === "gelios.units.current.read");
assert.deepEqual(telemetryCapability.request.query, {
incltrip: "true",
inclcntrs: "true",
inclsnsrs: "true",
incllsv: "true",
});
const telemetryMapping = geliosProviderPackageV7.mappingContracts.find((mapping) => mapping.id === "gelios.units.to.fleet.positions.current.v7");
assert.equal(telemetryMapping.derivations.sensor_readings.kind, "bounded_readings");
assert.equal(telemetryMapping.derivations.sensor_readings.parameters.maxReadings, 128);
assert.equal(telemetryMapping.fact.attributes.mileage_km.paths[0], "counters.mileage.value");
assert.equal(telemetryMapping.fact.attributes.engine_hours.paths[0], "counters.engineHours.value");
assert.equal(geliosProviderPackageV7.dataProducts.some((product) => product.id === "map.gelios-geofences.current.v1"), true);
const zonesProduct = geliosProviderPackageV6.dataProducts.find((product) => product.id === "map.gelios-geofences.current.v1");
const registeredZonesProduct = JSON.parse(await readFile(new URL(
"../../../services/external-data-plane/definitions/map.zones.current.v2.json",
import.meta.url,
), "utf8"));
assert.deepEqual(zonesProduct.semanticTypes, ["map.zone"]);
assert.equal(zonesProduct.fieldContracts.geometry.type, "geometry");
assert.equal(registeredZonesProduct.version, "2.0.0");
assert.deepEqual(registeredZonesProduct.fieldContracts.dataset_authority.enum, ["moscow-department-of-transport"]);
assert.deepEqual(registeredZonesProduct.fieldContracts.source_adapter.enum, [
"depttrans-mmap-snapshot-v1",
"depttrans-api-snapshot-v1",
]);
const zonesCapability = geliosProviderPackageV6.capabilities.find((capability) => capability.id === "gelios.geozones.current.read");
assert.equal(zonesCapability.request.query.pl, 100);
assert.equal(zonesCapability.request.query.po, 0);
assert.equal(zonesCapability.request.response.pagination.maxItems, 5000);
assert.equal(zonesCapability.request.response.pagination.totalPath, "paginationMetadata.totalCount");
const zonesMapping = geliosProviderPackageV6.mappingContracts.find((mapping) => mapping.id === "gelios.geozones.to.map.gelios-geofences.current.v1");
assert.deepEqual(zonesMapping.fact.observedAt.paths, ["updatedAt"]);
assert.equal(zonesMapping.fact.observedAt.fallback, "collection_received_at");
const strictProduct = geliosProviderPackageV5.dataProducts[0];
const registeredStrictProduct = JSON.parse(await readFile(new URL(
"../../../services/external-data-plane/definitions/fleet.positions.current.v4.json",
import.meta.url,
), "utf8"));
const geliosOntologyEntities = JSON.parse(await readFile(new URL(
"../../../services/ontology-core/catalog/domain-packages/gelios/entities.json",
import.meta.url,
), "utf8"));
assert.equal(geliosProviderPackageV5.id, "gelios.provider.v5");
assert.equal(strictProduct.id, "fleet.positions.current.v4");
assert.deepEqual(strictProduct, registeredStrictProduct);
assert.deepEqual(strictProduct.fieldContracts.signal_state.enum, ["active", "inactive"]);
assert.deepEqual(strictProduct.fieldContracts.movement_state.enum, ["moving", "stopped"]);
for (const ontologyEntityId of ["gelios.signal_state", "gelios.movement_state"]) {
const valueContract = geliosOntologyEntities.entities.find((entity) => entity.id === ontologyEntityId)?.valueContract;
assert.ok(valueContract, `${ontologyEntityId} must expose an Ontology valueContract`);
assert.deepEqual(
strictProduct.fieldContracts[valueContract.field].enum,
valueContract.values.map(({ value: state }) => state),
`${valueContract.field} Data Product enum must equal the Ontology value contract`,
);
}
const withInvalidClosedStateDefault = structuredClone(geliosProviderPackageV5);
withInvalidClosedStateDefault.mappingContracts[0].derivations.signal_state.default = "invented";
assert.equal(
validateProviderPackage(withInvalidClosedStateDefault).errors.includes(
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.signal_state.derivation.default_not_in_field_contract_enum",
),
true,
);
const withInvalidClosedStateRule = structuredClone(geliosProviderPackageV5);
withInvalidClosedStateRule.mappingContracts[0].derivations.movement_state.rules[0] = "integer_speed_gt_threshold.teleporting";
assert.equal(
validateProviderPackage(withInvalidClosedStateRule).errors.includes(
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.movement_state.derivation.rules[0]_not_in_field_contract_enum",
),
true,
);
const withOptionalRequiredState = structuredClone(geliosProviderPackageV5);
withOptionalRequiredState.mappingContracts[0].fact.attributes.signal_state.omitIfMissing = true;
assert.equal(
validateProviderPackage(withOptionalRequiredState).errors.includes(
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.signal_state_required_but_mapping_can_omit",
),
true,
);
const withMalformedFieldContract = structuredClone(geliosProviderPackageV5);
withMalformedFieldContract.dataProducts[0].fieldContracts.signal_state = null;
assert.doesNotThrow(() => validateProviderPackage(withMalformedFieldContract));
assert.equal(validateProviderPackage(withMalformedFieldContract).ok, false);
const withUnboundedNumericMapping = structuredClone(geliosProviderPackageV5);
delete withUnboundedNumericMapping.mappingContracts[0].fact.attributes.speed_kph.minimum;
assert.equal(
validateProviderPackage(withUnboundedNumericMapping).errors.includes(
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.speed_kph.minimum_must_match_field_contract",
),
true,
);
const withNonOmittingBoundedMapping = structuredClone(geliosProviderPackageV5);
withNonOmittingBoundedMapping.mappingContracts[0].fact.attributes.course_degrees.omitIfInvalid = false;
assert.equal(
validateProviderPackage(withNonOmittingBoundedMapping).errors.includes(
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.course_degrees.bounded_number_must_omit_invalid",
),
true,
);
const canonicalMonitoringProduct = geliosProviderPackageV4.dataProducts[0];
const canonicalMonitoringMapping = geliosProviderPackageV4.mappingContracts[0];
const registeredCanonicalMonitoringProduct = JSON.parse(await readFile(new URL(
"../../../services/external-data-plane/definitions/fleet.positions.current.v3.json",
import.meta.url,
), "utf8"));
assert.equal(geliosProviderPackageV4.id, "gelios.provider.v4");
assert.equal(geliosProviderPackageV4.version, "4.0.0");
assert.deepEqual(geliosProviderPackageV4.manifest.ontology, {
packageId: "gelios",
revision: "ontology.gelios.v1_1",
});
assert.deepEqual(geliosProviderPackageV4.collectionProfiles[0].capabilityIds, [
"gelios.monitoring_config.current.read",
"gelios.units.current.read",
]);
assert.equal(
geliosProviderPackageV4.capabilities.find((item) => item.id === "gelios.monitoring_config.current.read").request.path,
"/api/v1/users/me/monitoring-config",
);
assert.equal(
geliosProviderPackageV4.capabilities.find((item) => item.id === "gelios.units.current.read").request.query.incltrip,
"true",
);
assert.deepEqual(canonicalMonitoringProduct, registeredCanonicalMonitoringProduct);
assert.deepEqual(canonicalMonitoringProduct.fields.filter((field) => field.endsWith("_state")), [
"movement_state",
"signal_state",
]);
for (const forbiddenField of [
"availability_state",
"freshness_state",
"operational_status",
"position_state",
"state_policy_version",
]) {
assert.equal(canonicalMonitoringProduct.fields.includes(forbiddenField), false);
}
assert.deepEqual(canonicalMonitoringMapping.derivations.signal_state.rules, [
"last_message_missing.inactive",
"last_message_age_lt_resolved_monitoring_limit.active",
"otherwise.inactive",
]);
assert.equal(canonicalMonitoringMapping.derivations.signal_state.default, "inactive");
assert.equal(canonicalMonitoringMapping.derivations.signal_state.parameters.activeDurationPath, "signalActiveDuration");
assert.equal(
canonicalMonitoringMapping.derivations.signal_state.parameters.somewhatInactiveDurationPath,
"signalSomewhatInactiveDuration",
);
assert.equal(canonicalMonitoringMapping.derivations.signal_state.parameters.collectionProfileFallbackSeconds, 120);
assert.equal(
canonicalMonitoringMapping.derivations.signal_state.parameters.thresholdResolution,
"somewhatInactive_then_active_then_profileFallback",
);
assert.equal(canonicalMonitoringMapping.derivations.movement_state.parameters.movingThresholdKph, 2);
assert.equal(canonicalMonitoringMapping.derivations.movement_state.default, "stopped");
assert.deepEqual(
geliosProviderPackageV4.l2Templates[0].steps.map((step) => step.kind),
[
"collection_trigger",
"provider_request",
"provider_request",
"extract_items",
"semantic_mapping",
"data_product_publish",
],
);
const stateAwareProduct = geliosProviderPackageV3.dataProducts[0];
const stateAwareMapping = geliosProviderPackageV3.mappingContracts[0];
const registeredStateAwareProduct = JSON.parse(await readFile(new URL(
"../../../services/external-data-plane/definitions/fleet.positions.current.v2.json",
import.meta.url,
), "utf8"));
assert.equal(geliosProviderPackageV3.id, "gelios.provider.v3");
assert.equal(geliosProviderPackageV3.version, "3.0.0");
assert.equal(geliosProviderPackageV3.authModes[0].id, "gelios.rest-rotating-bearer.v3");
assert.deepEqual(geliosProviderPackageV3.authModes[0].transport, {
placement: "header",
name: "Authorization",
});
assert.deepEqual(geliosProviderPackageV3.authModes[0].tokenLifecycle, {
artifacts: ["access", "refresh"],
requestArtifact: "access",
refreshMode: "runtime_managed",
});
assert.equal(geliosProviderPackageV3.capabilities[0].request.baseUrl, "https://api.geliospro.com");
assert.equal(geliosProviderPackageV3.capabilities[0].request.path, "/api/v1/units");
assert.deepEqual(geliosProviderPackageV3.capabilities[0].request.query, {});
assert.equal(
geliosProviderPackageV3.l2Templates[0].credentialBindings.find((binding) => binding.role === "provider").authModeId,
"gelios.rest-rotating-bearer.v3",
);
assert.equal(stateAwareProduct.id, "fleet.positions.current.v2");
assert.equal(stateAwareProduct.version, "2.0.0");
assert.equal(stateAwareProduct.ontologyRevision, "ontology.map.moving_object.v2");
assert.deepEqual(stateAwareProduct, registeredStateAwareProduct);
assert.deepEqual(
["availability_state", "freshness_state", "motion_state", "position_state"].map((field) => (
stateAwareMapping.fact.attributes[field]
)),
[
{ derive: "availability_state" },
{ derive: "freshness_state" },
{ derive: "motion_state" },
{ derive: "position_state" },
],
);
assert.equal(stateAwareMapping.fact.attributes.state_policy_version.constant, "map-moving-object-state/v1");
assert.equal(stateAwareMapping.derivations.motion_state.parameters.movingThresholdKph, 1);
assert.equal(stateAwareMapping.derivations.position_state.parameters.minSatelliteCount, 4);
assert.equal(stateAwareMapping.derivations.position_state.parameters.maxHdop, 5);
assert.equal(stateAwareMapping.derivations.position_state.parameters.maxHorizontalAccuracyMeters, 100);
const geliosSdkAuth = geliosProviderPackageV2.authModes[0];
const geliosSdkUnitsRead = geliosProviderPackageV2.capabilities[0];
assert.equal(geliosProviderPackageV2.id, "gelios.provider.v2");
assert.equal(geliosProviderPackageV2.version, "2.0.0");
assert.equal(geliosSdkAuth.id, "gelios.sdk-query-token.v2");
assert.deepEqual(geliosSdkAuth.transport, { placement: "query", name: "token" });
assert.deepEqual(geliosSdkAuth.tokenLifecycle, {
artifacts: ["access"],
requestArtifact: "access",
refreshMode: "not_applicable",
});
assert.equal(geliosSdkUnitsRead.request.baseUrl, "https://admin.geliospro.com");
assert.equal(geliosSdkUnitsRead.request.path, "/sdk/");
assert.deepEqual(geliosSdkUnitsRead.request.query, { svc: "get_units", params: "{}" });
assert.equal(geliosProviderPackageV2.dataProducts[0].id, GELIOS_POSITIONS_DATA_PRODUCT_ID);
const geliosAuth = geliosProviderPackageV1.authModes[0];
const geliosUnitsRead = geliosProviderPackageV1.capabilities[0];
assert.equal(geliosAuth.id, "gelios.rest-bearer.v1");
assert.deepEqual(geliosAuth.transport, { placement: "header", name: "Authorization" });
assert.deepEqual(geliosAuth.tokenLifecycle, {
artifacts: ["access", "refresh"],
requestArtifact: "access",
refreshMode: "operator_managed",
});
assert.equal(geliosAuth.tokenLifecycle.artifacts.includes("writer"), false);
assert.equal(geliosUnitsRead.request.baseUrl, "https://api.geliospro.com");
assert.equal(geliosUnitsRead.request.path, "/api/v1/units");
assert.deepEqual(geliosUnitsRead.request.query, {});
const product = geliosProviderPackageV1.dataProducts[0];
const registeredProduct = JSON.parse(await readFile(new URL(
"../../../services/external-data-plane/definitions/fleet.positions.current.v1.json",
import.meta.url,
), "utf8"));
assert.equal(product.id, GELIOS_POSITIONS_DATA_PRODUCT_ID);
assert.equal(product.version, GELIOS_POSITIONS_DATA_PRODUCT_VERSION);
assert.equal(product.ontologyRevision, GELIOS_POSITIONS_ONTOLOGY_REVISION);
assert.equal(product.id, "fleet.positions.current.v1");
assert.equal(product.version, "1.0.0");
assert.equal(product.ontologyRevision, "ontology.map.moving_object.v1");
assert.deepEqual(product.fields, expectedFields);
assert.deepEqual(product, registeredProduct);
assert.doesNotThrow(() => normalizeDataProductDefinition(product));
assert.equal(product.fields.every((field) => /^[a-z][a-z0-9_]*$/.test(field)), true);
const mapping = geliosProviderPackageV1.mappingContracts[0];
assert.equal(mapping.target.dataProductId, product.id);
assert.equal(mapping.target.version, product.version);
assert.equal(mapping.target.ontologyRevision, product.ontologyRevision);
assert.deepEqual(
[...Object.keys(mapping.fact.attributes), "geometry"].sort(),
[...product.fields].sort(),
);
assert.equal(mapping.derivations.operational_status.parameters.staleAfterMs, 60000);
assert.deepEqual(mapping.derivations.quality_flags.default, []);
assert.equal(mapping.fact.observedAt.fallback, "collection_received_at");
assert.equal(GELIOS_UNIT_SOURCE_ID_PREFIX, "gelios-unit-");
assert.equal(mapping.fact.sourceId.prefix, GELIOS_UNIT_SOURCE_ID_PREFIX);
assert.equal(mapping.fact.sourceId.coerce, "string");
assert.equal(mapping.fact.geometry.longitude.paths.includes("lastMsg.lon"), true);
assert.equal(mapping.fact.geometry.latitude.paths.includes("lastMsg.lat"), true);
assert.equal(mapping.fact.attributes.speed_kph.paths.includes("lastMsg.speed"), true);
assert.equal(validateDataProductPublish(geliosUnitsCurrentFixtureV1.expectedPublish).ok, true);
assert.equal(
geliosUnitsCurrentFixtureV1.sourceResponse.units.length,
geliosUnitsCurrentFixtureV1.expectedPublish.facts.length,
"every entity returned for the bound credential remains in the publish fixture",
);
assert.deepEqual(
geliosUnitsCurrentFixtureV1.expectedPublish.facts.map((fact) => fact.sourceId),
geliosUnitsCurrentFixtureV1.sourceResponse.units.map(
(unit) => `${GELIOS_UNIT_SOURCE_ID_PREFIX}${String(unit.id)}`,
),
);
assert.equal(
geliosUnitsCurrentFixtureV1.expectedPublish.facts[1].sourceId,
"gelios-unit-001002",
"provider ID strings must remain byte-for-byte stable after the canonical prefix",
);
assert.equal(geliosUnitsCurrentFixtureV1.expectedPublish.facts[1].geometry, undefined);
assert.equal(geliosUnitsCurrentFixtureV1.expectedPublish.facts[1].attributes.operational_status, "no_position");
assert.equal(geliosUnitsCurrentFixtureV1.sourceResponse.units[1].lastMsg, undefined);
assert.equal(
geliosUnitsCurrentFixtureV1.expectedPublish.facts[1].observedAt,
geliosUnitsCurrentFixtureV1.collectionContext.receivedAt,
);
const realtimeProfileId = "gelios.positions.current.realtime.v1";
assert.equal(
geliosProviderPackageV1.collectionProfiles[0].cardinality.onExceed,
"require_partitioned_data_product",
);
const accountA = instantiateL2Connection(geliosProviderPackageV1, {
tenantId: "tenant-alpha",
connectionId: "gelios-account-alpha",
collectionProfileId: realtimeProfileId,
providerCredentialRef: "ndc-credref:provider-alpha-0001",
});
const accountB = instantiateL2Connection(geliosProviderPackageV1, {
tenantId: "tenant-beta",
connectionId: "gelios-account-beta",
collectionProfileId: realtimeProfileId,
providerCredentialRef: "ndc-credref:provider-beta-0001",
});
assert.equal(accountA.schemaVersion, L2_CONNECTION_INSTANCE_SCHEMA_VERSION);
assert.equal(accountA.package.id, accountB.package.id);
assert.equal(accountA.package.version, accountB.package.version);
assert.equal(accountA.l2TemplateId, accountB.l2TemplateId);
assert.notEqual(accountA.connectionId, accountB.connectionId);
assert.notEqual(accountA.credentialRefs.provider.reference, accountB.credentialRefs.provider.reference);
assert.equal(accountA.credentialRefs.provider.owner, "ndc_l2_credentials");
assert.deepEqual(Object.keys(accountA.credentialRefs), ["provider"]);
assert.deepEqual(accountA.systemBindings.publisher, {
role: "publisher",
owner: "ndc_l2_credentials",
management: "control_plane_managed",
nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
desiredState: "bound",
status: "unresolved",
});
assert.equal(accountA.scope.mode, "all_visible_to_credential");
assert.equal(accountA.scope.refresh, "each_collection_run");
assert.equal(Object.isFrozen(accountA), true);
assert.equal(Object.isFrozen(accountA.credentialRefs), true);
assert.equal(Object.isFrozen(accountA.systemBindings), true);
const withEntityAllowlist = structuredClone(geliosProviderPackageV1);
withEntityAllowlist.capabilities[0].entityScope.unitIds = ["gelios-unit-1001"];
assert.equal(
validateProviderPackage(withEntityAllowlist).errors.includes("capabilities[0].entityScope.unitIds_not_allowed"),
true,
);
const withGroupFilter = structuredClone(geliosProviderPackageV1);
withGroupFilter.collectionProfiles[0].entityScope.businessEntityFilter = "provider_group";
assert.equal(
validateProviderPackage(withGroupFilter).errors.includes("collectionProfiles[0].entityScope.businessEntityFilter_must_be_forbidden"),
true,
);
const withWrongRevision = structuredClone(geliosProviderPackageV1);
withWrongRevision.mappingContracts[0].target.ontologyRevision = "gelios.positions.v1";
assert.equal(
validateProviderPackage(withWrongRevision).errors.includes("mappingContract.gelios.units.to.fleet.positions.current.v1.target.ontologyRevision_must_match_data_product"),
true,
);
const withMissingDerivation = structuredClone(geliosProviderPackageV1);
delete withMissingDerivation.mappingContracts[0].derivations.position_valid;
assert.equal(
validateProviderPackage(withMissingDerivation).errors.includes("mappingContracts[0].derive_position_valid_missing_definition"),
true,
);
const withWrongFactSemanticType = structuredClone(geliosProviderPackageV1);
withWrongFactSemanticType.mappingContracts[0].fact.semanticType.constant = "map.other";
assert.equal(
validateProviderPackage(withWrongFactSemanticType).errors.includes(
"mappingContract.gelios.units.to.fleet.positions.current.v1.fact.semanticType_must_equal_target_constant",
),
true,
);
const nonGeospatialPackage = structuredClone(geliosProviderPackageV1);
nonGeospatialPackage.dataProducts[0].fields = ["display_name"];
nonGeospatialPackage.fieldPolicies[0].targetFields = ["display_name"];
delete nonGeospatialPackage.mappingContracts[0].fact.geometry;
nonGeospatialPackage.mappingContracts[0].fact.attributes = {
display_name: { strategy: "first_non_empty", paths: ["name"], coerce: "string" },
};
delete nonGeospatialPackage.mappingContracts[0].derivations;
assert.deepEqual(validateProviderPackage(nonGeospatialPackage), { ok: true, errors: [] });
for (const restrictedPath of ["phone", "phone.number", "lmsg"]) {
const withRestrictedMappingPath = structuredClone(geliosProviderPackageV1);
withRestrictedMappingPath.mappingContracts[0].fact.attributes.display_name.paths = [restrictedPath];
assert.equal(
validateProviderPackage(withRestrictedMappingPath).errors.includes(
"mappingContract.gelios.units.to.fleet.positions.current.v1.fact_source_path_restricted",
),
true,
);
}
const withFieldPolicyProductMismatch = structuredClone(geliosProviderPackageV1);
const secondProduct = structuredClone(withFieldPolicyProductMismatch.dataProducts[0]);
secondProduct.id = "fleet.positions.alternate.v1";
withFieldPolicyProductMismatch.dataProducts.push(secondProduct);
withFieldPolicyProductMismatch.manifest.dataProductIds.push(secondProduct.id);
withFieldPolicyProductMismatch.fieldPolicies[0].dataProductId = secondProduct.id;
assert.equal(
validateProviderPackage(withFieldPolicyProductMismatch).errors.includes(
"mappingContract.gelios.units.to.fleet.positions.current.v1.field_policy_data_product_mismatch",
),
true,
);
const withCamelCaseField = structuredClone(geliosProviderPackageV1);
withCamelCaseField.dataProducts[0].fields[12] = "speedKph";
assert.equal(validateProviderPackage(withCamelCaseField).ok, false);
const withSecretField = structuredClone(geliosProviderPackageV1);
withSecretField.dataProducts[0].fields[12] = "api_token";
withSecretField.fieldPolicies[0].targetFields[12] = "api_token";
withSecretField.mappingContracts[0].fact.attributes.api_token = { constant: "metadata-only" };
delete withSecretField.mappingContracts[0].fact.attributes.speed_kph;
const secretFieldValidation = validateProviderPackage(withSecretField);
assert.equal(secretFieldValidation.errors.includes("dataProducts[0].fields_must_not_contain_secret_fields"), true);
assert.equal(secretFieldValidation.errors.includes("fieldPolicies[0].targetFields_must_not_contain_secret_fields"), true);
assert.equal(secretFieldValidation.errors.includes("mappingContracts[0].fact.attributes.api_token_secret_field_not_allowed"), true);
const withRuntimeAccountState = structuredClone(geliosProviderPackageV1);
withRuntimeAccountState.tenantId = "must-not-live-in-package";
assert.equal(
validateProviderPackage(withRuntimeAccountState).errors.includes("providerPackage_must_not_contain_connection_instance_state"),
true,
);
const withPrimitiveCapability = structuredClone(geliosProviderPackageV1);
withPrimitiveCapability.capabilities.push(null);
assert.doesNotThrow(() => validateProviderPackage(withPrimitiveCapability));
assert.equal(validateProviderPackage(withPrimitiveCapability).ok, false);
const withMalformedNestedArrays = structuredClone(geliosProviderPackageV1);
withMalformedNestedArrays.collectionProfiles[0].capabilityIds = {};
withMalformedNestedArrays.l2Templates[0].credentialBindings = {};
withMalformedNestedArrays.l2Templates[0].steps = {};
assert.doesNotThrow(() => validateProviderPackage(withMalformedNestedArrays));
assert.equal(validateProviderPackage(withMalformedNestedArrays).ok, false);
const withCredentialInStaticQuery = structuredClone(geliosProviderPackageV1);
withCredentialInStaticQuery.authModes[0].transport = { placement: "query", name: "token" };
withCredentialInStaticQuery.capabilities[0].request.query.token = "plain-provider-key";
assert.equal(
validateProviderPackage(withCredentialInStaticQuery).errors.includes("capabilities[0].request.query.token_credential_parameter_not_allowed"),
true,
);
assert.equal(
validateProviderPackage(withCredentialInStaticQuery).errors.includes("capability.gelios.units.current.read.request.query_must_not_embed_credential_parameter"),
true,
);
const withCredentialInBaseUrl = structuredClone(geliosProviderPackageV1);
withCredentialInBaseUrl.capabilities[0].request.baseUrl = "https://user:pass@api.geliospro.com/?token=forbidden";
assert.equal(validateProviderPackage(withCredentialInBaseUrl).ok, false);
for (const unsafePath of ["/ok\nX-Evil: yes", "/back\\slash", "/query?x=1"]) {
const withUnsafeRequestPath = structuredClone(geliosProviderPackageV1);
withUnsafeRequestPath.capabilities[0].request.path = unsafePath;
assert.equal(validateProviderPackage(withUnsafeRequestPath).ok, false);
}
for (const unsafeCollectionPath of ["__proto__.polluted", "data.items[0]", "constructor.prototype"]) {
const withUnsafeCollectionPath = structuredClone(geliosProviderPackageV1);
withUnsafeCollectionPath.capabilities[0].request.response.collectionPaths = [unsafeCollectionPath];
assert.equal(validateProviderPackage(withUnsafeCollectionPath).ok, false);
}
const withUnsafeMappingPath = structuredClone(geliosProviderPackageV1);
withUnsafeMappingPath.mappingContracts[0].fact.attributes.display_name.paths = ["__proto__.polluted"];
assert.equal(validateProviderPackage(withUnsafeMappingPath).ok, false);
const withUnsafeCollectionCapability = structuredClone(geliosProviderPackageV1);
withUnsafeCollectionCapability.capabilities[0].classification = "write";
assert.equal(
validateProviderPackage(withUnsafeCollectionCapability).errors.includes("collectionProfile.gelios.positions.current.realtime.v1.capabilityIds_must_reference_implemented_safe_read"),
true,
);
const withUnexecutedSecondCapability = structuredClone(geliosProviderPackageV1);
const secondCapability = structuredClone(withUnexecutedSecondCapability.capabilities[0]);
secondCapability.id = "gelios.units.metadata.read";
withUnexecutedSecondCapability.capabilities.push(secondCapability);
withUnexecutedSecondCapability.manifest.capabilityIds.push(secondCapability.id);
for (const profile of withUnexecutedSecondCapability.collectionProfiles) {
profile.capabilityIds.push(secondCapability.id);
}
assert.equal(
validateProviderPackage(withUnexecutedSecondCapability).errors.includes(
"collectionProfile.gelios.positions.current.realtime.v1.template_capability_chain_mismatch",
),
true,
);
const withReorderedSteps = structuredClone(geliosProviderPackageV1);
[withReorderedSteps.l2Templates[0].steps[0], withReorderedSteps.l2Templates[0].steps[1]] = [
withReorderedSteps.l2Templates[0].steps[1],
withReorderedSteps.l2Templates[0].steps[0],
];
assert.equal(
validateProviderPackage(withReorderedSteps).errors.includes("l2Templates[0].steps_sequence_invalid"),
true,
);
const withUnknownConnectionParameters = structuredClone(geliosProviderPackageV1);
withUnknownConnectionParameters.l2Templates[0].connectionParameters = ["foo"];
assert.equal(
validateProviderPackage(withUnknownConnectionParameters).errors.includes(
"l2Templates[0].connectionParameters_must_match_v1_contract",
),
true,
);
const withCredentialAuthMismatch = structuredClone(geliosProviderPackageV1);
const secondAuthMode = structuredClone(withCredentialAuthMismatch.authModes[0]);
secondAuthMode.id = "gelios.rest-bearer.alternate.v1";
withCredentialAuthMismatch.authModes.push(secondAuthMode);
withCredentialAuthMismatch.manifest.authModeIds.push(secondAuthMode.id);
withCredentialAuthMismatch.l2Templates[0].credentialBindings[0].authModeId = secondAuthMode.id;
assert.equal(
validateProviderPackage(withCredentialAuthMismatch).errors.includes(
"l2Template.gelios.positions.current.l2.v1.provider_credential_auth_mode_mismatch",
),
true,
);
const withCallerManagedPublisher = structuredClone(geliosProviderPackageV1);
withCallerManagedPublisher.l2Templates[0].credentialBindings[1].management = "connection_input";
assert.equal(
validateProviderPackage(withCallerManagedPublisher).errors.includes(
"l2Templates[0].credentialBindings[1].publisher_binding_shape_invalid",
),
true,
);
const withUnknownTokenArtifact = structuredClone(geliosProviderPackageV1);
withUnknownTokenArtifact.authModes[0].tokenLifecycle.artifacts.push("writer");
assert.equal(
validateProviderPackage(withUnknownTokenArtifact).errors.includes(
"authModes[0].tokenLifecycle.artifacts_invalid",
),
true,
);
const withRefreshUsedForRequest = structuredClone(geliosProviderPackageV1);
withRefreshUsedForRequest.authModes[0].tokenLifecycle.requestArtifact = "refresh";
assert.equal(
validateProviderPackage(withRefreshUsedForRequest).errors.includes(
"authModes[0].tokenLifecycle.requestArtifact_must_be_access",
),
true,
);
const withUnmanagedRefreshArtifact = structuredClone(geliosProviderPackageV1);
withUnmanagedRefreshArtifact.authModes[0].tokenLifecycle.refreshMode = "not_applicable";
assert.equal(
validateProviderPackage(withUnmanagedRefreshArtifact).errors.includes(
"authModes[0].tokenLifecycle.refreshMode_required_for_refresh_artifact",
),
true,
);
for (const history of [{
mode: "none",
intervalMs: 60000,
strategy: "latest-per-entity-per-bucket",
retentionDays: 90,
}, {
mode: "sampled",
intervalMs: 24 * 60 * 60 * 1000 + 1,
strategy: "latest-per-entity-per-bucket",
retentionDays: 90,
}, {
mode: "all",
retentionDays: 3651,
}]) {
const withInvalidHistory = structuredClone(geliosProviderPackageV1);
withInvalidHistory.dataProducts[0].history = history;
assert.equal(validateProviderPackage(withInvalidHistory).ok, false);
assert.throws(() => normalizeDataProductDefinition(withInvalidHistory.dataProducts[0]));
}
const withBrokenArtifactChain = structuredClone(geliosProviderPackageV1);
withBrokenArtifactChain.l2Templates[0].steps[4].dataProductId = "unrelated.product.v1";
assert.equal(
validateProviderPackage(withBrokenArtifactChain).errors.includes("collectionProfile.gelios.positions.current.realtime.v1.template_data_product_mismatch"),
true,
);
const withSecretNamedDerivationParameter = structuredClone(geliosProviderPackageV1);
withSecretNamedDerivationParameter.mappingContracts[0].derivations.position_valid.parameters = {
accessToken: "opaque-provider-value",
};
assert.equal(
validateProviderPackage(withSecretNamedDerivationParameter).errors.includes(
"mappingContracts[0].derivations.position_valid.parameters.accessToken_secret_field_not_allowed",
),
true,
);
const withObjectConstant = structuredClone(geliosProviderPackageV1);
withObjectConstant.mappingContracts[0].fact.attributes.object_kind.constant = {
accessToken: "opaque-provider-value",
};
assert.equal(
validateProviderPackage(withObjectConstant).errors.includes(
"mappingContracts[0].fact.attributes.object_kind.constant_must_be_scalar_or_scalar_array",
),
true,
);
assert.throws(() => instantiateL2Connection(geliosProviderPackageV1, {
tenantId: "tenant-alpha",
connectionId: "gelios-account-alpha",
collectionProfileId: realtimeProfileId,
providerCredentialRef: "Bearer plaintext-must-not-cross-boundary",
}), /l2_connection_invalid/);
assert.throws(() => instantiateL2Connection(geliosProviderPackageV1, {
tenantId: "tenant-alpha",
connectionId: "gelios-account-alpha",
collectionProfileId: realtimeProfileId,
providerCredentialRef: "ndc-credref:provider-alpha-0001",
writerCredentialRef: "ndc-credref:caller-writer-forbidden",
}), /options\.writerCredentialRef_not_allowed/);
const serializedPackage = JSON.stringify(geliosProviderPackageV1);
assert.equal(serializedPackage.includes('"prefix":"unit-"'), false);
assert.equal(serializedPackage.includes("unitIds"), false);
assert.equal(serializedPackage.includes("tenant-alpha"), false);
assert.equal(serializedPackage.includes("gelios-gateway"), false);
assert.equal(serializedPackage.includes("writer_credential_ref"), false);
assert.equal(serializedPackage.includes("writerCapabilityByReferenceOnly"), false);
assert.equal(serializedPackage.includes("publisherBindingControlPlaneManaged\":true"), true);
assert.equal(serializedPackage.includes("providerSpecificServiceForbidden\":true"), true);
assert.deepEqual(
geliosProviderPackageV1.l2Templates[0].steps.map((step) => step.kind),
[
"collection_trigger",
"provider_request",
"extract_items",
"semantic_mapping",
"data_product_publish",
],
);
assert.equal(
geliosProviderPackageV1.l2Templates[0].steps.at(-1).nodeType,
"n8n-nodes-ndc.ndcDataProductPublish",
);
console.log("external-provider-contract provider-package: ok");