Skip to content

Commit

Permalink
Refactor duplicate pact tests in mockttp
Browse files Browse the repository at this point in the history
  • Loading branch information
jdewinne committed Apr 13, 2024
1 parent 003c0d9 commit 85d923a
Show file tree
Hide file tree
Showing 9 changed files with 388 additions and 391 deletions.
22 changes: 22 additions & 0 deletions dist/clusters.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,25 @@ export declare class ClusterVersion {
name: string;
version: string;
}
export declare class Addon {
id: string;
status: string;
object_store?: ObjectStore;
postgres?: Postgres;
}
export declare class ObjectStore {
bucket_name: string;
bucket_prefix: string;
service_account_name: string;
service_account_name_read_only: string;
service_account_namespace: string;
}
export declare class Postgres {
version: string;
instance_type: string;
disk_gib: number;
uri: string;
}
export declare class StatusError extends Error {
statusCode: number;
constructor(message: string, statusCode: number);
Expand All @@ -31,4 +50,7 @@ export declare function getKubeconfig(vendorPortalApi: VendorPortalApi, clusterI
export declare function removeCluster(vendorPortalApi: VendorPortalApi, clusterId: string): Promise<void>;
export declare function upgradeCluster(vendorPortalApi: VendorPortalApi, clusterId: string, k8sVersion: string): Promise<Cluster>;
export declare function getClusterVersions(vendorPortalApi: VendorPortalApi): Promise<ClusterVersion[]>;
export declare function createAddonObjectStore(vendorPortalApi: VendorPortalApi, clusterId: string, bucketName: string): Promise<Addon>;
export declare function createAddonPostgres(vendorPortalApi: VendorPortalApi, clusterId: string, version?: string, instanceType?: string, diskGib?: number): Promise<Addon>;
export declare function pollForAddonStatus(vendorPortalApi: VendorPortalApi, clusterId: string, addonId: string, expectedStatus: string, timeout?: number, sleeptimeMs?: number): Promise<Addon>;
export {};
134 changes: 133 additions & 1 deletion dist/clusters.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getClusterVersions = exports.upgradeCluster = exports.removeCluster = exports.getKubeconfig = exports.pollForStatus = exports.createClusterWithLicense = exports.createCluster = exports.StatusError = exports.ClusterVersion = exports.Cluster = void 0;
exports.pollForAddonStatus = exports.createAddonPostgres = exports.createAddonObjectStore = exports.getClusterVersions = exports.upgradeCluster = exports.removeCluster = exports.getKubeconfig = exports.pollForStatus = exports.createClusterWithLicense = exports.createCluster = exports.StatusError = exports.Postgres = exports.ObjectStore = exports.Addon = exports.ClusterVersion = exports.Cluster = void 0;
class Cluster {
}
exports.Cluster = Cluster;
class ClusterVersion {
}
exports.ClusterVersion = ClusterVersion;
class Addon {
}
exports.Addon = Addon;
class ObjectStore {
}
exports.ObjectStore = ObjectStore;
class Postgres {
}
exports.Postgres = Postgres;
class StatusError extends Error {
constructor(message, statusCode) {
super(message);
Expand Down Expand Up @@ -169,3 +178,126 @@ async function getClusterVersions(vendorPortalApi) {
return clusterVersions;
}
exports.getClusterVersions = getClusterVersions;
async function createAddonObjectStore(vendorPortalApi, clusterId, bucketName) {
const http = await vendorPortalApi.client();
const uri = `${vendorPortalApi.endpoint}/cluster/${clusterId}/addon/objectstore`;
const reqBody = {
"bucket": bucketName,
};
const res = await http.post(uri, JSON.stringify(reqBody));
if (res.message.statusCode != 201) {
let body = "";
try {
body = await res.readBody();
}
catch (err) {
// ignore
}
throw new Error(`Failed to queue addon create: Server responded with ${res.message.statusCode}: ${body}`);
}
const body = JSON.parse(await res.readBody());
var addon = { id: body.id, status: body.status };
if (body.object_store) {
addon.object_store = { bucket_name: body.object_store.bucket_name, bucket_prefix: body.object_store.bucket_prefix,
service_account_name: body.object_store.service_account_name, service_account_name_read_only: body.object_store.service_account_name_read_only,
service_account_namespace: body.object_store.service_account_namespace };
}
return addon;
}
exports.createAddonObjectStore = createAddonObjectStore;
async function createAddonPostgres(vendorPortalApi, clusterId, version, instanceType, diskGib) {
const http = await vendorPortalApi.client();
const uri = `${vendorPortalApi.endpoint}/cluster/${clusterId}/addon/postgres`;
const reqBody = {};
if (version) {
reqBody['version'] = version;
}
if (instanceType) {
reqBody['instance_type'] = instanceType;
}
if (diskGib) {
reqBody['disk_gib'] = diskGib;
}
const res = await http.post(uri, JSON.stringify(reqBody));
if (res.message.statusCode != 201) {
let body = "";
try {
body = await res.readBody();
}
catch (err) {
// ignore
}
throw new Error(`Failed to queue addon create: Server responded with ${res.message.statusCode}: ${body}`);
}
const body = JSON.parse(await res.readBody());
var addon = { id: body.id, status: body.status };
if (body.postgres) {
addon.postgres = { uri: body.postgres.uri, version: body.postgres.version, instance_type: body.postgres.instance_type, disk_gib: body.postgres.disk_gib };
}
return addon;
}
exports.createAddonPostgres = createAddonPostgres;
async function pollForAddonStatus(vendorPortalApi, clusterId, addonId, expectedStatus, timeout = 120, sleeptimeMs = 5000) {
// get addons from the api, look for the status of the id to be ${status}
// if it's not ${status}, sleep for 5 seconds and try again
// if it is ${status}, return the addon with that status
await new Promise(f => setTimeout(f, sleeptimeMs)); // sleep for sleeptimeMs seconds before polling as the addon takes a few seconds to start provisioning
// iterate for timeout/sleeptime times
const iterations = timeout * 1000 / sleeptimeMs;
for (let i = 0; i < iterations; i++) {
try {
const addonDetails = await getAddonDetails(vendorPortalApi, clusterId, addonId);
if (addonDetails.status === expectedStatus) {
return addonDetails;
}
// Once state is "error", it will never change. So we can shortcut polling.
if (addonDetails.status === "error") {
throw new Error(`Addon has entered error state.`);
}
console.debug(`Cluster status is ${addonDetails.status}, sleeping for ${sleeptimeMs / 1000} seconds`);
}
catch (err) {
if (err instanceof StatusError) {
if (err.statusCode >= 500) {
// 5xx errors are likely transient, so we should retry
console.debug(`Got HTTP error with status ${err.statusCode}, sleeping for ${sleeptimeMs / 1000} seconds`);
}
else {
console.debug(`Got HTTP error with status ${err.statusCode}, exiting`);
throw err;
}
}
else {
throw err;
}
}
await new Promise(f => setTimeout(f, sleeptimeMs));
}
throw new Error(`Addon did not reach state ${expectedStatus} within ${timeout} seconds`);
}
exports.pollForAddonStatus = pollForAddonStatus;
async function getAddonDetails(vendorPortalApi, clusterId, addonId) {
const http = await vendorPortalApi.client();
const uri = `${vendorPortalApi.endpoint}/cluster/${clusterId}/addons`;
const res = await http.get(uri);
if (res.message.statusCode != 200) {
throw new StatusError(`Failed to get addon: Server responded with ${res.message.statusCode}`, res.message.statusCode);
}
const body = JSON.parse(await res.readBody());
for (const addon of body.addons) {
if (addon.id === addonId) {
var addonObj = { id: addon.id, status: addon.status };
if (addon.object_store) {
addonObj.object_store = { bucket_name: addon.object_store.bucket_name, bucket_prefix: addon.object_store.bucket_prefix,
service_account_name: addon.object_store.service_account_name, service_account_name_read_only: addon.object_store.service_account_name_read_only,
service_account_namespace: addon.object_store.service_account_namespace };
}
if (addon.postgres) {
addonObj.postgres = { uri: addon.postgres.uri, version: addon.postgres.version, instance_type: addon.postgres.instance_type,
disk_gib: addon.postgres.disk_gib };
}
return addonObj;
}
}
throw new Error(`Addon with id ${addonId} not found`);
}
164 changes: 31 additions & 133 deletions dist/clusters.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,146 +40,44 @@ describe('ClusterService', () => {
});
});
});
describe('ClusterService with tags', () => {
beforeAll(() => globalThis.provider.setup());
afterEach(() => globalThis.provider.verify());
afterAll(() => globalThis.provider.finalize());
test('should return cluster with tags', () => {
describe('ClusterService use cases', () => {
const mockServer = mockttp.getLocal();
const apiClient = new configuration_1.VendorPortalApi();
apiClient.apiToken = "abcd1234";
beforeAll(async () => {
await mockServer.start();
apiClient.endpoint = `http://localhost:${mockServer.port}`;
});
afterAll(async () => {
await mockServer.stop();
});
test('should return cluster with tags', async () => {
const expectedCluster = { cluster: { name: "cluster1", id: "1234abcd", status: "provisioning" } };
const reqBody = {
name: "cluster1",
kubernetes_distribution: "kind",
kubernetes_version: "v1.25.1",
ttl: "10m",
tags: [
{
key: "foo",
value: "bar"
}
]
};
globalThis.provider.addInteraction({
state: 'cluster created',
uponReceiving: 'a request for creating a cluster with tags',
withRequest: {
method: 'POST',
path: '/cluster',
body: reqBody,
},
willRespondWith: {
status: 201,
headers: { 'Content-Type': 'application/json' },
body: expectedCluster
}
});
const apiClient = new configuration_1.VendorPortalApi();
apiClient.apiToken = "abcd1234";
apiClient.endpoint = globalThis.provider.mockService.baseUrl;
await mockServer.forPost("/cluster").thenReply(201, JSON.stringify(expectedCluster));
const tags = [{ key: "foo", value: "bar" }];
return (0, _1.createCluster)(apiClient, "cluster1", "kind", "v1.25.1", "10m", undefined, undefined, undefined, undefined, undefined, undefined, tags)
.then(cluster => {
expect(cluster.name).toEqual(expectedCluster.cluster.name);
expect(cluster.id).toEqual(expectedCluster.cluster.id);
expect(cluster.status).toEqual(expectedCluster.cluster.status);
});
const cluster = await (0, _1.createCluster)(apiClient, "cluster1", "kind", "v1.25.1", "10m", undefined, undefined, undefined, undefined, undefined, undefined, tags);
expect(cluster.name).toEqual(expectedCluster.cluster.name);
expect(cluster.id).toEqual(expectedCluster.cluster.id);
expect(cluster.status).toEqual(expectedCluster.cluster.status);
});
});
describe('ClusterService with nodegroups', () => {
beforeAll(() => globalThis.provider.setup());
afterEach(() => globalThis.provider.verify());
afterAll(() => globalThis.provider.finalize());
test('should return cluster with nodegroups', () => {
test('should return cluster with nodegroups', async () => {
const expectedCluster = { cluster: { name: "cluster1", id: "1234abcd", status: "provisioning" } };
const reqBody = {
name: "cluster1",
kubernetes_distribution: "eks",
kubernetes_version: "v1.29",
ttl: "10m",
node_groups: [
{
name: "foo",
node_count: 3,
instance_type: "r1.medium",
disk_gib: 100
}
]
};
globalThis.provider.addInteraction({
state: 'cluster created',
uponReceiving: 'a request for creating a cluster with nodegroups',
withRequest: {
method: 'POST',
path: '/cluster',
body: reqBody,
},
willRespondWith: {
status: 201,
headers: { 'Content-Type': 'application/json' },
body: expectedCluster
}
});
const apiClient = new configuration_1.VendorPortalApi();
apiClient.apiToken = "abcd1234";
apiClient.endpoint = globalThis.provider.mockService.baseUrl;
await mockServer.forPost("/cluster").thenReply(201, JSON.stringify(expectedCluster));
const nodegroups = [{ name: "foo", node_count: 3, instance_type: "r1.medium", disk_gib: 100 }];
return (0, _1.createCluster)(apiClient, "cluster1", "eks", "v1.29", "10m", undefined, undefined, undefined, undefined, undefined, nodegroups)
.then(cluster => {
expect(cluster.name).toEqual(expectedCluster.cluster.name);
expect(cluster.id).toEqual(expectedCluster.cluster.id);
expect(cluster.status).toEqual(expectedCluster.cluster.status);
});
const cluster = await (0, _1.createCluster)(apiClient, "cluster1", "eks", "v1.29", "10m", undefined, undefined, undefined, undefined, undefined, nodegroups);
expect(cluster.name).toEqual(expectedCluster.cluster.name);
expect(cluster.id).toEqual(expectedCluster.cluster.id);
expect(cluster.status).toEqual(expectedCluster.cluster.status);
});
});
describe('ClusterService with license_id', () => {
beforeAll(() => globalThis.provider.setup());
afterEach(() => globalThis.provider.verify());
afterAll(() => globalThis.provider.finalize());
test('should return cluster with license_id', () => {
test('should return cluster with license_id', async () => {
const expectedCluster = { cluster: { name: "cluster1", id: "1234abcd", status: "provisioning" } };
const reqBody = {
name: "cluster1",
kubernetes_distribution: "embedded-cluster",
kubernetes_version: "",
ttl: "10m",
license_id: "license1",
};
globalThis.provider.addInteraction({
state: 'cluster created',
uponReceiving: 'a request for creating a cluster with license_id',
withRequest: {
method: 'POST',
path: '/cluster',
body: reqBody,
},
willRespondWith: {
status: 201,
headers: { 'Content-Type': 'application/json' },
body: expectedCluster
}
});
const apiClient = new configuration_1.VendorPortalApi();
apiClient.apiToken = "abcd1234";
apiClient.endpoint = globalThis.provider.mockService.baseUrl;
return (0, _1.createClusterWithLicense)(apiClient, "cluster1", "embedded-cluster", "", "license1", "10m")
.then(cluster => {
expect(cluster.name).toEqual(expectedCluster.cluster.name);
expect(cluster.id).toEqual(expectedCluster.cluster.id);
expect(cluster.status).toEqual(expectedCluster.cluster.status);
});
});
});
describe('upgradeCluster', () => {
const mockServer = mockttp.getLocal();
const apiClient = new configuration_1.VendorPortalApi();
apiClient.apiToken = "abcd1234";
beforeEach(async () => {
await mockServer.start();
apiClient.endpoint = `http://localhost:${mockServer.port}`;
});
afterEach(async () => {
mockServer.stop();
await mockServer.forPost("/cluster").thenReply(201, JSON.stringify(expectedCluster));
const cluster = await (0, _1.createClusterWithLicense)(apiClient, "cluster1", "embedded-cluster", "", "license1", "10m");
expect(cluster.name).toEqual(expectedCluster.cluster.name);
expect(cluster.id).toEqual(expectedCluster.cluster.id);
expect(cluster.status).toEqual(expectedCluster.cluster.status);
});
it("upgrade a kurl cluster", async () => {
test("upgrade a kurl cluster", async () => {
const expectedUpgradeResponse = {};
await mockServer.forPost("/cluster/1234abcd/upgrade").thenReply(200, JSON.stringify(expectedUpgradeResponse));
await mockServer.forGet("/cluster/1234abcd").thenReply(200, JSON.stringify({ cluster: { id: "1234abcd", status: "upgrading" } }));
Expand All @@ -197,7 +95,7 @@ describe('pollForCluster', () => {
apiClient.endpoint = `http://localhost:${mockServer.port}`;
});
afterEach(async () => {
mockServer.stop();
await mockServer.stop();
});
test('should eventually return success with expected status', async () => {
const expectedCluster = { id: "1234abcd", name: "cluster1", status: "running" };
Expand Down
2 changes: 1 addition & 1 deletion dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { VendorPortalApi } from './configuration';
export { getApplicationDetails } from './applications';
export { Channel, createChannel, getChannelDetails, archiveChannel } from './channels';
export { ClusterVersion, createCluster, createClusterWithLicense, pollForStatus, getKubeconfig, removeCluster, upgradeCluster, getClusterVersions } from './clusters';
export { ClusterVersion, createCluster, createClusterWithLicense, pollForStatus, getKubeconfig, removeCluster, upgradeCluster, getClusterVersions, createAddonObjectStore, createAddonPostgres, pollForAddonStatus } from './clusters';
export { KubernetesDistribution, archiveCustomer, createCustomer, getUsedKubernetesDistributions } from './customers';
export { Release, CompatibilityResult, createRelease, createReleaseFromChart, promoteRelease, reportCompatibilityResult } from './releases';
Loading

0 comments on commit 85d923a

Please sign in to comment.