fix: make command constraint replay restart-safe
This commit is contained in:
@@ -16,6 +16,6 @@ alter table device_management_command_receipts
|
|||||||
'edge.ensure',
|
'edge.ensure',
|
||||||
'route.ensure',
|
'route.ensure',
|
||||||
'enrollment_intent.ensure'
|
'enrollment_intent.ensure'
|
||||||
));
|
)) not valid;
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
|||||||
@@ -20,6 +20,6 @@ alter table device_management_command_receipts
|
|||||||
'device.transfer',
|
'device.transfer',
|
||||||
'discovery.reject',
|
'discovery.reject',
|
||||||
'discovery.expire'
|
'discovery.expire'
|
||||||
));
|
)) not valid;
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ alter table device_management_command_receipts
|
|||||||
'discovery.expire',
|
'discovery.expire',
|
||||||
'device_credential_binding.upsert',
|
'device_credential_binding.upsert',
|
||||||
'device_credential_binding.revoke'
|
'device_credential_binding.revoke'
|
||||||
));
|
)) not valid;
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
|||||||
@@ -26,6 +26,6 @@ alter table device_management_command_receipts
|
|||||||
'device_binding.revoke',
|
'device_binding.revoke',
|
||||||
'device_configuration_revision.create',
|
'device_configuration_revision.create',
|
||||||
'device_configuration_desired.set'
|
'device_configuration_desired.set'
|
||||||
));
|
)) not valid;
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
|||||||
@@ -2,6 +2,36 @@ import assert from "node:assert/strict";
|
|||||||
import { readFile } from "node:fs/promises";
|
import { readFile } from "node:fs/promises";
|
||||||
import test from "node:test";
|
import test from "node:test";
|
||||||
|
|
||||||
|
const replayedIntermediateConstraintMigrations = Object.freeze([
|
||||||
|
"005_device_registry_commands.sql",
|
||||||
|
"007_device_lifecycle_commands.sql",
|
||||||
|
"009_device_sensitive_reference_commands.sql",
|
||||||
|
"011_device_control_resource_commands.sql",
|
||||||
|
]);
|
||||||
|
const finalCommandKindMigration = "014_device_registry_profile_commands.sql";
|
||||||
|
const finalCommandKinds = Object.freeze([
|
||||||
|
"owner_scope.ensure",
|
||||||
|
"project.ensure",
|
||||||
|
"collection.ensure",
|
||||||
|
"project_grant.upsert",
|
||||||
|
"adapter_package.ensure",
|
||||||
|
"adapter_version.register",
|
||||||
|
"model_profile.register",
|
||||||
|
"edge.ensure",
|
||||||
|
"route.ensure",
|
||||||
|
"enrollment_intent.ensure",
|
||||||
|
"device.claim",
|
||||||
|
"device.update",
|
||||||
|
"device.transfer",
|
||||||
|
"discovery.reject",
|
||||||
|
"discovery.expire",
|
||||||
|
"device_credential_binding.upsert",
|
||||||
|
"device_credential_binding.revoke",
|
||||||
|
"device_binding.ensure",
|
||||||
|
"device_binding.revoke",
|
||||||
|
"device_configuration_revision.create",
|
||||||
|
"device_configuration_desired.set",
|
||||||
|
]);
|
||||||
const migrationUrl = new URL(
|
const migrationUrl = new URL(
|
||||||
"../migrations/003_device_management_commands.sql",
|
"../migrations/003_device_management_commands.sql",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
@@ -37,3 +67,33 @@ test("repository applies management migration after project access", async () =>
|
|||||||
assert.notEqual(managementIndex, -1);
|
assert.notEqual(managementIndex, -1);
|
||||||
assert.ok(projectAccessIndex < managementIndex);
|
assert.ok(projectAccessIndex < managementIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("replayed intermediate command constraints do not revalidate historical receipts", async () => {
|
||||||
|
for (const migrationFile of replayedIntermediateConstraintMigrations) {
|
||||||
|
const sql = await readFile(
|
||||||
|
new URL(`../migrations/${migrationFile}`, import.meta.url),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.match(
|
||||||
|
sql,
|
||||||
|
/add constraint device_management_command_receipts_command_kind_check[\s\S]+not valid;/,
|
||||||
|
`${migrationFile} must not reject receipts introduced by a later migration`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("final replayed command constraint validates every current command kind", async () => {
|
||||||
|
const sql = await readFile(
|
||||||
|
new URL(`../migrations/${finalCommandKindMigration}`, import.meta.url),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.doesNotMatch(sql, /not valid;/);
|
||||||
|
for (const commandKind of finalCommandKinds) {
|
||||||
|
assert.ok(
|
||||||
|
sql.includes(`'${commandKind}'`),
|
||||||
|
`${finalCommandKindMigration} must validate ${commandKind}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user