Skip to content

Commit

Permalink
Merge pull request #5036 from systeminit/brit/bug-679-add-api-test-fo…
Browse files Browse the repository at this point in the history
…r-authoring-flow

Add an API test for basic authoring scenario
  • Loading branch information
britmyerss authored Nov 27, 2024
2 parents f2d09cc + 3386086 commit beafa8b
Show file tree
Hide file tree
Showing 5 changed files with 800 additions and 173 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/run-api-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ jobs:
echo "workspace_ids found to be $workspace_ids"
workspace_count=$(echo "$workspace_ids" | tr ',' '\n' | wc -l)
# Validate that the number of workspace IDs matches the number of tests
if [ "$test_count" -ne "$workspace_count" ]; then
echo "Error: The number of workspace IDs ($workspace_count) does not match the number of tests ($test_count)."
# Validate that the number of workspace IDs is greater than or equal to the number of tests
if [ "$workspace_count" -lt "$test_count" ]; then
echo "Error: The number of workspace IDs ($workspace_count) is less than the number of tests ($test_count)."
exit 1
fi
Expand Down
71 changes: 71 additions & 0 deletions bin/si-api-test/sdf_api_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ export const ROUTES = {
path: () => `/component/set_type`,
method: "POST",
},
dvu_roots: {
path: (vars: ROUTE_VARS) =>
`/diagram/dvu_roots?visibility_change_set_pk=${vars.changeSetId}&workspaceId=${vars.workspaceId}`,
method: "GET",
},

// Component Management -------------------------------------------------------
delete_component: {
Expand Down Expand Up @@ -85,6 +90,19 @@ export const ROUTES = {
path: () => `/variant/create_variant`,
method: "POST",
},
save_variant: {
path: () => `/variant/save_variant`,
method: "POST",
},
regenerate_variant: {
path: () => `/variant/regenerate_variant`,
method: "POST",
},
get_variant: {
path: (vars: ROUTE_VARS) =>
`/v2/workspaces/${vars.workspaceId}/change-sets/${vars.changeSetId}/schema-variants/${vars.schemaVariantId}`,
method: "GET",
},

// Action Management -----------------------------------------------------------
action_list: {
Expand All @@ -106,6 +124,26 @@ export const ROUTES = {
`/v2/workspaces/${vars.workspaceId}/change-sets/${vars.changeSetId}/funcs`,
method: "GET",
},
create_func: {
path: (vars: ROUTE_VARS) =>
`/v2/workspaces/${vars.workspaceId}/change-sets/${vars.changeSetId}/funcs`,
method: "POST",
},
create_func_arg: {
path: (vars: ROUTE_VARS) =>
`/v2/workspaces/${vars.workspaceId}/change-sets/${vars.changeSetId}/funcs/${vars.funcId}/arguments`,
method: "POST",
},
create_func_binding: {
path: (vars: ROUTE_VARS) =>
`/v2/workspaces/${vars.workspaceId}/change-sets/${vars.changeSetId}/funcs/${vars.funcId}/bindings`,
method: "PUT",
},
update_func_code: {
path: (vars: ROUTE_VARS) =>
`/v2/workspaces/${vars.workspaceId}/change-sets/${vars.changeSetId}/funcs/${vars.funcId}/code`,
method: "PUT",
},

// Websockets -----------------------------------------
workspace_updates_ws: {
Expand Down Expand Up @@ -243,6 +281,39 @@ export class SdfApiClient {
dvuListener.listen();
}

public async waitForDVURoots(
changeSetId: string,
interval_ms: number,
timeout_ms: number,
): Promise<void> {
console.log(`Waiting on DVUs for ${this.workspaceId}...`);
const dvuPromise = new Promise<void>((resolve) => {
const interval = setInterval(async () => {
const remainingRoots = await this.call({ route: "dvu_roots", routeVars: { changeSetId } });
if (remainingRoots?.count === 0) {
console.log(`All DVUs for ${this.workspaceId} finished!`);
clearInterval(interval);
resolve();
} else {
console.log(
`Waiting for DVUs in workspace ${this.workspaceId} to finish, ${remainingRoots?.count} remain...`,
);
}
}, interval_ms);
});

const timeoutPromise = new Promise<void>((_, reject) => {
setTimeout(() => {
console.log(
`Timeout reached while waiting for DVUs in workspace ${this.workspaceId}.`,
);
reject(new Error("Timeout while waiting for DVUs to finish."));
}, timeout_ms);
});

return Promise.race([dvuPromise, timeoutPromise]);
}

public async waitForDVUs(
interval_ms: number,
timeout_ms: number,
Expand Down
240 changes: 240 additions & 0 deletions bin/si-api-test/test_helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { SdfApiClient } from "./sdf_api_client.ts";
import assert from "node:assert";
import { ulid } from "https://deno.land/x/[email protected]/mod.ts";


export function sleep(ms: number) {
const natural_ms = Math.max(0, Math.floor(ms));
Expand Down Expand Up @@ -87,3 +89,241 @@ export async function runWithTemporaryChangeset(
throw err;
}
}

export const nilId = "00000000000000000000000000";

// Diagram Helpers ------------------------------------------------------------


export async function getQualificationSummary(sdf: SdfApiClient, changeSetId: string) {
return await sdf.call({
route: "qualification_summary",
routeVars: {
workspaceId: sdf.workspaceId,
changeSetId,
},
});
}

export async function getActions(sdf: SdfApiClient, changeSetId: string) {
return await sdf.call({
route: "action_list",
routeVars: {
workspaceId: sdf.workspaceId,
changeSetId,
},
});
}

export async function getFuncs(sdf: SdfApiClient, changeSetId: string) {
return await sdf.call({
route: "func_list",
routeVars: {
workspaceId: sdf.workspaceId,
changeSetId,
},
});
}


// Prop Helpers ------------------------------------------------------------


export async function getPropertyEditor(
sdf: SdfApiClient,
changeSetId: string,
componentId: string,
) {
const values = await sdf.call({
route: "get_property_values",
routeVars: {
componentId,
changeSetId,
},
});
assert(typeof values?.values === "object", "Expected prop values");
assert(typeof values?.childValues === "object", "Expected prop childValues:");

const schema = await sdf.call({
route: "get_property_schema",
routeVars: {
componentId,
changeSetId,
},
});
assert(typeof schema?.rootPropId === "string", "Expected rootPropId");
assert(typeof schema?.props === "object", "Expected props");
assert(typeof schema?.childProps === "object", "Expected childProps list");

return {
values,
schema,
};
}

export async function setAttributeValue(
sdf: SdfApiClient,
changeSetId: string,
componentId: string,
attributeValueId: string,
parentAttributeValueId: string,
propId: string,
value: unknown,
) {
const updateValuePayload = {
visibility_change_set_pk: changeSetId,
componentId,
attributeValueId,
parentAttributeValueId,
propId,
value,
isForSecret: false,
};

await sdf.call({
route: "update_property_value",
body: updateValuePayload,
});
}

export function attributeValueIdForPropPath(
propPath: string,
propList: any[],
attributeValuesView: {
values: any[];
childValues: any[];
},
) {
const prop = propList.find((p) => p.path === propPath);
assert(prop, `Expected to find ${propPath} prop`);

let attributeValueId;
let value;
for (const attributeValue in attributeValuesView.values) {
if (attributeValuesView.values[attributeValue]?.propId === prop.id) {
attributeValueId = attributeValue;
value = attributeValuesView.values[attributeValue]?.value;
}
}
assert(attributeValueId, "Expected source attribute value");

let parentAttributeValueId;
for (const attributeValue in attributeValuesView?.childValues) {
const avChildren = attributeValuesView?.childValues[attributeValue] ?? [];
if (avChildren.includes(attributeValueId)) {
parentAttributeValueId = attributeValue;
}
}
assert(parentAttributeValueId, "Expected parent of source attribute value");

return {
attributeValueId,
parentAttributeValueId,
propId: prop.id,
value,
};
}


// Schema Variant Helpers ------------------------------------------------------------

export async function createAsset(
sdf: SdfApiClient,
changeSetId: string,
name: string,
): Promise<string> {
const createAssetPayload = {
visibility_change_set_pk: changeSetId,
name,
color: "#AAFF00",
};

const createResp = await sdf.call({ route: "create_variant", body: createAssetPayload });
const schemaVariantId = createResp?.schemaVariantId;
assert(schemaVariantId, "Expected to get a schema variant id after creation");
return schemaVariantId;
}

export async function updateAssetCode(
sdf: SdfApiClient,
changeSetId: string,
schemaVariantId: string,
newCode: string,
): Promise<string> {
// Get variant
const variant = await sdf.call({
route: "get_variant", routeVars: {
workspaceId: sdf.workspaceId,
changeSetId,
schemaVariantId,
},
});
// update variant
const updateVariantBody = {
visibility_change_set_pk: changeSetId,
code: newCode,
variant,
};

const saveResp = await sdf.call({
route: "save_variant",
body: updateVariantBody,
});
const success = saveResp.success;
assert(success, "save was successful");

const regenBody = {
visibility_change_set_pk: changeSetId,
variant,
};

const regenerateResp = await sdf.call({
route: "regenerate_variant",
body: regenBody,
});

const maybeNewId = regenerateResp.schemaVariantId;
assert(maybeNewId, "Expected to get a schema variant id after regenerate");
return maybeNewId;
}


// Component Helpers ------------------------------------------------------------

export async function createComponent(
sdf: SdfApiClient,
changeSetId: string,
schemaVariantId: string,
x: number,
y: number,
parentId?: string,
newCreateComponentApi?: boolean,
): Promise<string> {
const parentArgs = parentId ? { parentId } : {};
const payload = {
schemaType: newCreateComponentApi ? "installed" : undefined,
schemaVariantId,
x: x.toString(),
y: y.toString(),
visibility_change_set_pk: changeSetId,
workspaceId: sdf.workspaceId,
...parentArgs,
};
const createResp = await sdf.call({
route: "create_component",
body: payload,
});
const componentId = createResp?.componentId;
assert(componentId, "Expected to get a component id after creation");

// Run side effect calls
await Promise.all([
getQualificationSummary(sdf, changeSetId),
getActions(sdf, changeSetId),
getFuncs(sdf, changeSetId),
getPropertyEditor(sdf, changeSetId, componentId),
]);

return componentId;
}

Loading

0 comments on commit beafa8b

Please sign in to comment.