delta validation and error handling done
This commit is contained in:
parent
969e6ddc10
commit
ba9cbc220d
359
__tests__/delta-validation.ts
Normal file
359
__tests__/delta-validation.ts
Normal file
@ -0,0 +1,359 @@
|
||||
import { DeltaV1, DeltaV2 } from "../src/delta";
|
||||
import {
|
||||
InvalidDeltaFormatError,
|
||||
MissingRequiredFieldError,
|
||||
InvalidPointerError,
|
||||
validateDeltaNetworkImageV1,
|
||||
validateDeltaNetworkImageV2
|
||||
} from "../src/delta-validation";
|
||||
|
||||
describe("Delta Validation", () => {
|
||||
describe("Invalid Delta Formats", () => {
|
||||
describe("DeltaV1 validation", () => {
|
||||
it("should throw error for non-object delta", () => {
|
||||
expect(() => validateDeltaNetworkImageV1(null)).toThrow(InvalidDeltaFormatError);
|
||||
expect(() => validateDeltaNetworkImageV1("string")).toThrow(InvalidDeltaFormatError);
|
||||
expect(() => validateDeltaNetworkImageV1(123)).toThrow(InvalidDeltaFormatError);
|
||||
expect(() => validateDeltaNetworkImageV1([])).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
|
||||
it("should throw error for invalid ID types", () => {
|
||||
const invalidDeltas = [
|
||||
{ id: null, timeCreated: 123, host: "host", creator: "creator", pointers: [] },
|
||||
{ id: 123, timeCreated: 123, host: "host", creator: "creator", pointers: [] },
|
||||
{ id: "", timeCreated: 123, host: "host", creator: "creator", pointers: [] },
|
||||
{ id: {}, timeCreated: 123, host: "host", creator: "creator", pointers: [] }
|
||||
];
|
||||
|
||||
invalidDeltas.forEach(delta => {
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for invalid timestamp", () => {
|
||||
const invalidDeltas = [
|
||||
{ id: "id", timeCreated: "123", host: "host", creator: "creator", pointers: [] },
|
||||
{ id: "id", timeCreated: -123, host: "host", creator: "creator", pointers: [] },
|
||||
{ id: "id", timeCreated: 0, host: "host", creator: "creator", pointers: [] },
|
||||
{ id: "id", timeCreated: null, host: "host", creator: "creator", pointers: [] }
|
||||
];
|
||||
|
||||
invalidDeltas.forEach(delta => {
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for invalid host/creator", () => {
|
||||
const invalidDeltas = [
|
||||
{ id: "id", timeCreated: 123, host: null, creator: "creator", pointers: [] },
|
||||
{ id: "id", timeCreated: 123, host: "", creator: "creator", pointers: [] },
|
||||
{ id: "id", timeCreated: 123, host: 123, creator: "creator", pointers: [] },
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: null, pointers: [] },
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: "", pointers: [] },
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: 123, pointers: [] }
|
||||
];
|
||||
|
||||
invalidDeltas.forEach(delta => {
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for non-array pointers", () => {
|
||||
const invalidDeltas = [
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: null },
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: {} },
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: "pointers" },
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: 123 }
|
||||
];
|
||||
|
||||
invalidDeltas.forEach(delta => {
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for empty pointers array", () => {
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: [] };
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
|
||||
it("should throw error for invalid pointer structure", () => {
|
||||
const invalidPointers = [
|
||||
[null],
|
||||
["string"],
|
||||
[123],
|
||||
[{ localContext: null, target: "target" }],
|
||||
[{ localContext: "", target: "target" }],
|
||||
[{ localContext: 123, target: "target" }],
|
||||
[{ localContext: "context", target: undefined }],
|
||||
[{ localContext: "context", target: {} }],
|
||||
[{ localContext: "context", target: [] }]
|
||||
];
|
||||
|
||||
invalidPointers.forEach(pointers => {
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", creator: "creator", pointers };
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(InvalidPointerError);
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for invalid targetContext", () => {
|
||||
const invalidPointers = [
|
||||
[{ localContext: "context", target: "target", targetContext: null }],
|
||||
[{ localContext: "context", target: "target", targetContext: "" }],
|
||||
[{ localContext: "context", target: "target", targetContext: 123 }],
|
||||
[{ localContext: "context", target: "target", targetContext: {} }]
|
||||
];
|
||||
|
||||
invalidPointers.forEach(pointers => {
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", creator: "creator", pointers };
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(InvalidPointerError);
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for pointer consistency violation", () => {
|
||||
// If targetContext exists, target must be a string (reference)
|
||||
const pointers = [{ localContext: "context", target: 123, targetContext: "property" }];
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", creator: "creator", pointers };
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(InvalidPointerError);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DeltaV2 validation", () => {
|
||||
it("should throw error for non-object delta", () => {
|
||||
expect(() => validateDeltaNetworkImageV2(null)).toThrow(InvalidDeltaFormatError);
|
||||
expect(() => validateDeltaNetworkImageV2("string")).toThrow(InvalidDeltaFormatError);
|
||||
expect(() => validateDeltaNetworkImageV2(123)).toThrow(InvalidDeltaFormatError);
|
||||
expect(() => validateDeltaNetworkImageV2([])).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
|
||||
it("should throw error for invalid pointers object", () => {
|
||||
const invalidDeltas = [
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: null },
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: [] },
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: "pointers" },
|
||||
{ id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: 123 }
|
||||
];
|
||||
|
||||
invalidDeltas.forEach(delta => {
|
||||
expect(() => validateDeltaNetworkImageV2(delta)).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for empty pointers object", () => {
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", creator: "creator", pointers: {} };
|
||||
expect(() => validateDeltaNetworkImageV2(delta)).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
|
||||
it("should throw error for invalid pointer keys", () => {
|
||||
const invalidPointers = [
|
||||
{ "": "value" }
|
||||
];
|
||||
|
||||
invalidPointers.forEach(pointers => {
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", creator: "creator", pointers };
|
||||
expect(() => validateDeltaNetworkImageV2(delta)).toThrow(InvalidPointerError);
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for invalid pointer values", () => {
|
||||
const invalidPointers = [
|
||||
{ key: undefined },
|
||||
{ key: [] },
|
||||
{ key: true },
|
||||
{ key: false }
|
||||
];
|
||||
|
||||
invalidPointers.forEach(pointers => {
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", creator: "creator", pointers };
|
||||
expect(() => validateDeltaNetworkImageV2(delta)).toThrow(InvalidPointerError);
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw error for invalid reference format", () => {
|
||||
const invalidReferences = [
|
||||
{ key: {} }, // Empty reference
|
||||
{ key: { ref1: "val1", ref2: "val2" } }, // Multiple keys
|
||||
{ key: { "": "value" } }, // Empty key
|
||||
{ key: { ref: "" } }, // Empty value
|
||||
{ key: { ref: 123 } }, // Non-string value
|
||||
{ key: { ref: null } } // Null value
|
||||
];
|
||||
|
||||
invalidReferences.forEach(pointers => {
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", creator: "creator", pointers };
|
||||
expect(() => validateDeltaNetworkImageV2(delta)).toThrow(InvalidPointerError);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Missing Required Fields", () => {
|
||||
describe("DeltaV1", () => {
|
||||
it("should throw MissingRequiredFieldError for missing id", () => {
|
||||
const delta = { timeCreated: 123, host: "host", creator: "creator", pointers: [] };
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(MissingRequiredFieldError);
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(/id/);
|
||||
});
|
||||
|
||||
it("should throw MissingRequiredFieldError for missing timeCreated", () => {
|
||||
const delta = { id: "id", host: "host", creator: "creator", pointers: [] };
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(MissingRequiredFieldError);
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(/timeCreated/);
|
||||
});
|
||||
|
||||
it("should throw MissingRequiredFieldError for missing host", () => {
|
||||
const delta = { id: "id", timeCreated: 123, creator: "creator", pointers: [] };
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(MissingRequiredFieldError);
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(/host/);
|
||||
});
|
||||
|
||||
it("should throw MissingRequiredFieldError for missing creator", () => {
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", pointers: [] };
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(MissingRequiredFieldError);
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(/creator/);
|
||||
});
|
||||
|
||||
it("should throw MissingRequiredFieldError for missing pointers", () => {
|
||||
const delta = { id: "id", timeCreated: 123, host: "host", creator: "creator" };
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(MissingRequiredFieldError);
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).toThrow(/pointers/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DeltaV2", () => {
|
||||
it("should throw MissingRequiredFieldError for all missing fields", () => {
|
||||
const requiredFields = ["id", "timeCreated", "host", "creator", "pointers"];
|
||||
|
||||
requiredFields.forEach(field => {
|
||||
const delta: Record<string, unknown> = {
|
||||
id: "id",
|
||||
timeCreated: 123,
|
||||
host: "host",
|
||||
creator: "creator",
|
||||
pointers: { key: "value" }
|
||||
};
|
||||
delete delta[field];
|
||||
|
||||
expect(() => validateDeltaNetworkImageV2(delta)).toThrow(MissingRequiredFieldError);
|
||||
expect(() => validateDeltaNetworkImageV2(delta)).toThrow(new RegExp(field));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Valid Delta Formats", () => {
|
||||
it("should accept valid DeltaV1", () => {
|
||||
const validDeltas = [
|
||||
{
|
||||
id: "uuid-123",
|
||||
timeCreated: 123456789,
|
||||
host: "host1",
|
||||
creator: "creator1",
|
||||
pointers: [{ localContext: "name", target: "Alice" }]
|
||||
},
|
||||
{
|
||||
id: "uuid-456",
|
||||
timeCreated: 987654321,
|
||||
host: "host2",
|
||||
creator: "creator2",
|
||||
pointers: [
|
||||
{ localContext: "name", target: "Bob" },
|
||||
{ localContext: "age", target: 25 },
|
||||
{ localContext: "active", target: null }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "uuid-789",
|
||||
timeCreated: 111111111,
|
||||
host: "host3",
|
||||
creator: "creator3",
|
||||
pointers: [{ localContext: "friend", target: "user123", targetContext: "friendOf" }]
|
||||
}
|
||||
];
|
||||
|
||||
validDeltas.forEach(delta => {
|
||||
expect(() => validateDeltaNetworkImageV1(delta)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept valid DeltaV2", () => {
|
||||
const validDeltas = [
|
||||
{
|
||||
id: "uuid-123",
|
||||
timeCreated: 123456789,
|
||||
host: "host1",
|
||||
creator: "creator1",
|
||||
pointers: { name: "Alice" }
|
||||
},
|
||||
{
|
||||
id: "uuid-456",
|
||||
timeCreated: 987654321,
|
||||
host: "host2",
|
||||
creator: "creator2",
|
||||
pointers: {
|
||||
name: "Bob",
|
||||
age: 25,
|
||||
active: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "uuid-789",
|
||||
timeCreated: 111111111,
|
||||
host: "host3",
|
||||
creator: "creator3",
|
||||
pointers: { friend: { user123: "friendOf" } }
|
||||
}
|
||||
];
|
||||
|
||||
validDeltas.forEach(delta => {
|
||||
expect(() => validateDeltaNetworkImageV2(delta)).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Delta class integration", () => {
|
||||
it("should validate when creating DeltaV1 from network image", () => {
|
||||
const invalidDelta = {
|
||||
id: "id",
|
||||
timeCreated: "not-a-number",
|
||||
host: "host",
|
||||
creator: "creator",
|
||||
pointers: [{ localContext: "name", target: "value" }]
|
||||
};
|
||||
|
||||
expect(() => DeltaV1.fromNetworkImage(invalidDelta as never)).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
|
||||
it("should validate when creating DeltaV2 from network image", () => {
|
||||
const invalidDelta = {
|
||||
id: "id",
|
||||
timeCreated: 123,
|
||||
host: "",
|
||||
creator: "creator",
|
||||
pointers: { name: "value" }
|
||||
};
|
||||
|
||||
expect(() => DeltaV2.fromNetworkImage(invalidDelta as never)).toThrow(InvalidDeltaFormatError);
|
||||
});
|
||||
|
||||
it("should accept valid network images", () => {
|
||||
const validV1 = {
|
||||
id: "uuid-123",
|
||||
timeCreated: 123456789,
|
||||
host: "host1",
|
||||
creator: "creator1",
|
||||
pointers: [{ localContext: "name", target: "Alice" }]
|
||||
};
|
||||
|
||||
const validV2 = {
|
||||
id: "uuid-456",
|
||||
timeCreated: 987654321,
|
||||
host: "host2",
|
||||
creator: "creator2",
|
||||
pointers: { name: "Bob" }
|
||||
};
|
||||
|
||||
expect(() => DeltaV1.fromNetworkImage(validV1)).not.toThrow();
|
||||
expect(() => DeltaV2.fromNetworkImage(validV2)).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
188
src/delta-validation.ts
Normal file
188
src/delta-validation.ts
Normal file
@ -0,0 +1,188 @@
|
||||
import { DeltaID, PointerTarget, DeltaNetworkImageV1, DeltaNetworkImageV2, PointersV2 } from "./delta";
|
||||
import { CreatorID, HostID, Timestamp } from "./types";
|
||||
|
||||
// Custom error types for delta operations
|
||||
export class DeltaValidationError extends Error {
|
||||
constructor(message: string, public field?: string) {
|
||||
super(message);
|
||||
this.name = "DeltaValidationError";
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidDeltaFormatError extends DeltaValidationError {
|
||||
constructor(message: string, field?: string) {
|
||||
super(message, field);
|
||||
this.name = "InvalidDeltaFormatError";
|
||||
}
|
||||
}
|
||||
|
||||
export class MissingRequiredFieldError extends DeltaValidationError {
|
||||
constructor(field: string) {
|
||||
super(`Missing required field: ${field}`, field);
|
||||
this.name = "MissingRequiredFieldError";
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidPointerError extends DeltaValidationError {
|
||||
constructor(message: string, pointerIndex?: number) {
|
||||
super(message, pointerIndex !== undefined ? `pointer[${pointerIndex}]` : undefined);
|
||||
this.name = "InvalidPointerError";
|
||||
}
|
||||
}
|
||||
|
||||
// Validation functions
|
||||
export function validateDeltaId(id: unknown): id is DeltaID {
|
||||
if (typeof id !== "string" || id.length === 0) {
|
||||
throw new InvalidDeltaFormatError("Delta ID must be a non-empty string", "id");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateTimestamp(timestamp: unknown, field: string): timestamp is Timestamp {
|
||||
if (typeof timestamp !== "number" || timestamp <= 0) {
|
||||
throw new InvalidDeltaFormatError(`${field} must be a positive number`, field);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateHostId(host: unknown): host is HostID {
|
||||
if (typeof host !== "string" || host.length === 0) {
|
||||
throw new InvalidDeltaFormatError("Host ID must be a non-empty string", "host");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateCreatorId(creator: unknown): creator is CreatorID {
|
||||
if (typeof creator !== "string" || creator.length === 0) {
|
||||
throw new InvalidDeltaFormatError("Creator ID must be a non-empty string", "creator");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validatePointerTarget(target: unknown): target is PointerTarget {
|
||||
if (target !== null && typeof target !== "string" && typeof target !== "number") {
|
||||
throw new InvalidPointerError("Pointer target must be string, number, or null");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validatePointerV1(pointer: unknown, index: number): pointer is { localContext: string; target: PointerTarget; targetContext?: string } {
|
||||
if (!pointer || typeof pointer !== "object" || Array.isArray(pointer)) {
|
||||
throw new InvalidPointerError(`Pointer at index ${index} must be an object`, index);
|
||||
}
|
||||
|
||||
const p = pointer as Record<string, unknown>;
|
||||
|
||||
if (typeof p.localContext !== "string" || p.localContext.length === 0) {
|
||||
throw new InvalidPointerError(`Pointer at index ${index} must have a non-empty localContext`, index);
|
||||
}
|
||||
|
||||
validatePointerTarget(p.target);
|
||||
|
||||
if (p.targetContext !== undefined &&
|
||||
(typeof p.targetContext !== "string" || p.targetContext.length === 0)) {
|
||||
throw new InvalidPointerError(`Pointer at index ${index} targetContext must be a non-empty string if present`, index);
|
||||
}
|
||||
|
||||
// Validate pointer consistency: if targetContext exists, target must be a string (reference)
|
||||
if (p.targetContext && typeof p.target !== "string") {
|
||||
throw new InvalidPointerError(`Pointer at index ${index} with targetContext must have string target (reference)`, index);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validatePointersV1(pointers: unknown): pointers is Array<{ localContext: string; target: PointerTarget; targetContext?: string }> {
|
||||
if (!Array.isArray(pointers)) {
|
||||
throw new InvalidDeltaFormatError("Pointers must be an array", "pointers");
|
||||
}
|
||||
|
||||
if (pointers.length === 0) {
|
||||
throw new InvalidDeltaFormatError("Delta must have at least one pointer", "pointers");
|
||||
}
|
||||
|
||||
(pointers as unknown[]).forEach((pointer, index) => validatePointerV1(pointer, index));
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validatePointersV2(pointers: unknown): pointers is PointersV2 {
|
||||
if (!pointers || typeof pointers !== "object" || Array.isArray(pointers)) {
|
||||
throw new InvalidDeltaFormatError("Pointers must be an object", "pointers");
|
||||
}
|
||||
|
||||
const keys = Object.keys(pointers);
|
||||
if (keys.length === 0) {
|
||||
throw new InvalidDeltaFormatError("Delta must have at least one pointer", "pointers");
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(pointers)) {
|
||||
if (key.length === 0) {
|
||||
throw new InvalidPointerError("Pointer key must be a non-empty string");
|
||||
}
|
||||
|
||||
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "object") {
|
||||
throw new InvalidPointerError(`Invalid pointer value for key '${key}'`);
|
||||
}
|
||||
|
||||
// If value is an object (Reference), validate it
|
||||
if (value && typeof value === "object") {
|
||||
const refKeys = Object.keys(value);
|
||||
if (refKeys.length !== 1) {
|
||||
throw new InvalidPointerError(`Reference pointer '${key}' must have exactly one key-value pair`);
|
||||
}
|
||||
const [refKey, refValue] = Object.entries(value)[0];
|
||||
if (typeof refKey !== "string" || refKey.length === 0) {
|
||||
throw new InvalidPointerError(`Reference key in pointer '${key}' must be a non-empty string`);
|
||||
}
|
||||
if (typeof refValue !== "string" || refValue.length === 0) {
|
||||
throw new InvalidPointerError(`Reference value in pointer '${key}' must be a non-empty string`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateDeltaNetworkImageV1(delta: unknown): delta is DeltaNetworkImageV1 {
|
||||
if (!delta || typeof delta !== "object" || Array.isArray(delta)) {
|
||||
throw new InvalidDeltaFormatError("Delta must be an object");
|
||||
}
|
||||
|
||||
// Check required fields
|
||||
if (!("id" in delta)) throw new MissingRequiredFieldError("id");
|
||||
if (!("timeCreated" in delta)) throw new MissingRequiredFieldError("timeCreated");
|
||||
if (!("host" in delta)) throw new MissingRequiredFieldError("host");
|
||||
if (!("creator" in delta)) throw new MissingRequiredFieldError("creator");
|
||||
if (!("pointers" in delta)) throw new MissingRequiredFieldError("pointers");
|
||||
|
||||
// Validate field types
|
||||
validateDeltaId(delta.id);
|
||||
validateTimestamp(delta.timeCreated, "timeCreated");
|
||||
validateHostId(delta.host);
|
||||
validateCreatorId(delta.creator);
|
||||
validatePointersV1(delta.pointers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateDeltaNetworkImageV2(delta: unknown): delta is DeltaNetworkImageV2 {
|
||||
if (!delta || typeof delta !== "object" || Array.isArray(delta)) {
|
||||
throw new InvalidDeltaFormatError("Delta must be an object");
|
||||
}
|
||||
|
||||
// Check required fields
|
||||
if (!("id" in delta)) throw new MissingRequiredFieldError("id");
|
||||
if (!("timeCreated" in delta)) throw new MissingRequiredFieldError("timeCreated");
|
||||
if (!("host" in delta)) throw new MissingRequiredFieldError("host");
|
||||
if (!("creator" in delta)) throw new MissingRequiredFieldError("creator");
|
||||
if (!("pointers" in delta)) throw new MissingRequiredFieldError("pointers");
|
||||
|
||||
// Validate field types
|
||||
validateDeltaId(delta.id);
|
||||
validateTimestamp(delta.timeCreated, "timeCreated");
|
||||
validateHostId(delta.host);
|
||||
validateCreatorId(delta.creator);
|
||||
validatePointersV2(delta.pointers);
|
||||
|
||||
return true;
|
||||
}
|
@ -3,6 +3,7 @@ import Debug from 'debug';
|
||||
import microtime from 'microtime';
|
||||
import {PeerAddress} from "./peers";
|
||||
import {CreatorID, DomainEntityID, HostID, PropertyID, Timestamp, TransactionID} from "./types";
|
||||
import {validateDeltaNetworkImageV1, validateDeltaNetworkImageV2} from "./delta-validation";
|
||||
const debug = Debug('rz:delta');
|
||||
|
||||
export type DeltaID = string;
|
||||
@ -75,6 +76,7 @@ export class DeltaV1 extends DeltaNetworkImageV1 {
|
||||
}
|
||||
|
||||
static fromNetworkImage(delta: DeltaNetworkImageV1) {
|
||||
validateDeltaNetworkImageV1(delta);
|
||||
return new DeltaV1(delta);
|
||||
}
|
||||
}
|
||||
@ -98,6 +100,7 @@ export class DeltaV2 extends DeltaNetworkImageV2 {
|
||||
}
|
||||
|
||||
static fromNetworkImage(delta: DeltaNetworkImageV2) {
|
||||
validateDeltaNetworkImageV2(delta);
|
||||
return new DeltaV2(delta);
|
||||
}
|
||||
|
||||
|
12
todo.md
12
todo.md
@ -4,12 +4,12 @@ This document tracks work needed to achieve full specification compliance, organ
|
||||
|
||||
## Phase 1: Foundation (Prerequisites)
|
||||
|
||||
### 1.1 Delta Validation & Error Handling
|
||||
- [ ] Implement delta structure validation
|
||||
- [ ] Add tests for invalid delta formats
|
||||
- [ ] Add tests for required fields (id, created, pointers)
|
||||
- [ ] Implement proper error types for delta operations
|
||||
- [ ] Add validation for pointer consistency
|
||||
### 1.1 Delta Validation & Error Handling ✅
|
||||
- [x] Implement delta structure validation
|
||||
- [x] Add tests for invalid delta formats
|
||||
- [x] Add tests for required fields (id, created, pointers)
|
||||
- [x] Implement proper error types for delta operations
|
||||
- [x] Add validation for pointer consistency
|
||||
|
||||
### 1.2 Complete Transaction Support
|
||||
- [ ] Implement transaction-based filtering in lossless views
|
||||
|
Loading…
x
Reference in New Issue
Block a user