Skip to content

Commit

Permalink
test(sdl): cover endpoints validation
Browse files Browse the repository at this point in the history
  • Loading branch information
ygrishajev committed May 10, 2024
1 parent e1b055a commit 5ff4fee
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 1,056 deletions.
101 changes: 101 additions & 0 deletions src/sdl/SDL/SDL.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,107 @@ describe("SDL", () => {
});
});

describe("endpoints", () => {
it("should resolve with valid endpoints", () => {
const endpointName = faker.lorem.word();
const endpoint = {
[endpointName]: {
kind: "ip"
}
};
const yml = readBasicSdl({ endpoint });
const sdl = SDL.fromString(yml, "beta3", "sandbox");

expect(sdl.manifest()).toMatchObject([
{
services: [
{
resources: {
endpoints: {
1: {
kind: 2,
sequence_number: 1
}
}
},
expose: [
{
ip: endpointName,
endpointSequenceNumber: 1
}
]
}
]
}
]);
expect(sdl.groups()).toMatchObject([
{
resources: [
{
resource: {
endpoints: {
1: {
kind: 2,
sequence_number: 1
}
}
}
}
]
}
]);
});

it("should throw provided an invalid endpoint name", () => {
const endpointName = faker.number.int().toString();
const endpoint = {
[endpointName]: {
kind: "ip"
}
};
const yml = readBasicSdl({ endpoint });

expect(() => SDL.fromString(yml, "beta3", "sandbox")).toThrowError(new SdlValidationError(`Endpoint named "${endpointName}" is not a valid name.`));
});

it("should throw provided no endpoint kind", () => {
const endpointName = faker.lorem.word();
const endpoint = {
[endpointName]: {}
};
const yml = readBasicSdl({ endpoint });

expect(() => SDL.fromString(yml, "beta3", "sandbox")).toThrowError(new SdlValidationError(`Endpoint named "${endpointName}" has no kind.`));
});

it("should throw provided invalid endpoint kind", () => {
const endpointName = faker.lorem.word();
const endpointKind = faker.lorem.word();
const endpoint = {
[endpointName]: {
kind: endpointKind
}
};
const yml = readBasicSdl({ endpoint });

expect(() => SDL.fromString(yml, "beta3", "sandbox")).toThrowError(
new SdlValidationError(`Endpoint named "${endpointName}" has an unknown kind "${endpointKind}".`)
);
});

it("should throw when endpoint is unused", () => {
const endpointName = faker.lorem.word();
const endpoint = {
[endpointName]: {
kind: "ip"
}
};
const yml = readBasicSdl({ endpoint, endpointRef: undefined });

expect(() => SDL.fromString(yml, "beta3", "sandbox")).toThrowError(new SdlValidationError(`Endpoint ${endpointName} declared but never used.`));
});
});

describe("service image credentials", () => {
it("should resolve a service with valid credentials", () => {
const credentials = {
Expand Down
21 changes: 15 additions & 6 deletions src/sdl/SDL/SDL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class SDL {
// to v2 or v3 SDL only after being validated
const data = YAML.load(yaml) as v3Sdl;

for (const [name, profile] of Object.entries(data.profiles.compute)) {
for (const [name, profile] of Object.entries(data.profiles.compute || {})) {
this.validateGPU(name, profile.resources.gpu);
this.validateStorage(name, profile.resources.storage);
}
Expand Down Expand Up @@ -157,7 +157,7 @@ export class SDL {
// TODO: this should really be cast to unknown, then assigned
// to v2 or v3 SDL only after being validated
const v3data = this.data as v3Sdl;
Object.entries(v3data.profiles.compute).forEach(([name, { resources }]) => {
Object.entries(v3data.profiles.compute || {}).forEach(([name, { resources }]) => {
if ("gpu" in resources) {
SDL.validateGPU(name, resources.gpu);
}
Expand All @@ -173,6 +173,7 @@ export class SDL {
});

this.validateDenom();
this.validateEndpointsUtility();
}

private validateDenom() {
Expand Down Expand Up @@ -215,12 +216,12 @@ export class SDL {

Object.keys(this.data.deployment[serviceName]).forEach(deploymentName => {
const serviceDeployment = this.data.deployment[serviceName][deploymentName];
const compute = this.data.profiles.compute[serviceDeployment.profile];
const infra = this.data.profiles.placement[deploymentName];
const compute = this.data.profiles.compute?.[serviceDeployment.profile];
const infra = this.data.profiles.placement?.[deploymentName];

SdlValidationError.assert(infra, `The placement "${deploymentName}" is not defined in the "placement" section.`);
SdlValidationError.assert(
infra.pricing[serviceDeployment.profile],
infra.pricing?.[serviceDeployment.profile],
`The pricing for the "${serviceDeployment.profile}" profile is not defined in the "${deploymentName}" "placement" definition.`
);
SdlValidationError.assert(compute, `The compute requirements for the "${serviceDeployment.profile}" profile are not defined in the "compute" section.`);
Expand Down Expand Up @@ -253,6 +254,14 @@ export class SDL {
});
}

private validateEndpointsUtility() {
if (this.data.endpoints) {
Object.keys(this.data.endpoints).forEach(endpoint => {
SdlValidationError.assert(this.endpointsUsed.has(endpoint), `Endpoint ${endpoint} declared but never used.`);
});
}
}

services() {
if (this.data) {
return this.data.services;
Expand Down Expand Up @@ -849,7 +858,7 @@ export class SDL {
const pricing = infra.pricing[svcdepl.profile];
const price = {
...pricing,
amount: pricing.amount.toString()
amount: pricing.amount?.toString()
};

let group = groups.get(placementName);
Expand Down
Loading

0 comments on commit 5ff4fee

Please sign in to comment.