86 lines
2.8 KiB
TypeScript
86 lines
2.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
ContractContributionError,
|
|
composeContractContributions,
|
|
type InstalledContractContribution,
|
|
} from "../../src/contracts/external-contract-runtime.ts";
|
|
import { TEST_CONTRACT_CONTRIBUTION } from "../helpers/external-contract-fixture.ts";
|
|
|
|
function contribution(
|
|
contributionId: string,
|
|
http: InstalledContractContribution["http"],
|
|
): InstalledContractContribution & Readonly<{ contributionId: string }> {
|
|
return Object.freeze({
|
|
...TEST_CONTRACT_CONTRIBUTION,
|
|
contributionId,
|
|
http: Object.freeze([...http]),
|
|
});
|
|
}
|
|
|
|
function captureContributionError(operation: () => unknown): ContractContributionError {
|
|
try {
|
|
operation();
|
|
} catch (error) {
|
|
expect(error).toBeInstanceOf(ContractContributionError);
|
|
return error as ContractContributionError;
|
|
}
|
|
throw new Error("Expected contract composition to fail.");
|
|
}
|
|
|
|
describe("external contract contribution composition", () => {
|
|
it("allows one feature to install multiple uniquely identified contributions", () => {
|
|
const operations = TEST_CONTRACT_CONTRIBUTION.http;
|
|
const composed = composeContractContributions([
|
|
contribution("test-read-contracts", operations.slice(0, 2)),
|
|
contribution("test-command-contracts", operations.slice(2)),
|
|
]);
|
|
|
|
expect(composed.contributions).toHaveLength(2);
|
|
expect(composed.httpByOperationId.size).toBe(3);
|
|
});
|
|
|
|
it("rejects duplicate contribution identities even across different feature entries", () => {
|
|
const first = contribution(
|
|
"test-contracts",
|
|
TEST_CONTRACT_CONTRIBUTION.http.slice(0, 1),
|
|
);
|
|
const second = contribution(
|
|
"test-contracts",
|
|
TEST_CONTRACT_CONTRIBUTION.http.slice(1),
|
|
);
|
|
|
|
const error = captureContributionError(() =>
|
|
composeContractContributions([first, second]),
|
|
);
|
|
expect(error.reason).toContain("duplicate contributionId");
|
|
});
|
|
|
|
it("maps malformed source values to the closed composition error", () => {
|
|
const malformed = {
|
|
...contribution("malformed-source", []),
|
|
source: null,
|
|
} as unknown as InstalledContractContribution;
|
|
|
|
const error = captureContributionError(() =>
|
|
composeContractContributions([malformed]),
|
|
);
|
|
expect(error.reason).toContain("source");
|
|
});
|
|
|
|
it("rejects method and body vocabulary outside the runtime protocol", () => {
|
|
const installed = TEST_CONTRACT_CONTRIBUTION.http[0]!;
|
|
const malformedOperation = {
|
|
...installed,
|
|
contract: { ...installed.contract, method: "TRACE" },
|
|
} as unknown as InstalledContractContribution["http"][number];
|
|
|
|
const error = captureContributionError(() =>
|
|
composeContractContributions([
|
|
contribution("invalid-http-vocabulary", [malformedOperation]),
|
|
]),
|
|
);
|
|
expect(error.reason).toContain("method");
|
|
});
|
|
});
|