From 7e0d862a50758a6ffc52a356ebc3d7ec85d366ab Mon Sep 17 00:00:00 2001 From: mchittupolu Date: Thu, 7 Aug 2025 09:16:09 -0700 Subject: [PATCH] Added integration and Mocked Tests --- examples/index.js | 138 +++-- package-lock.json | 2 +- tests/integration/actions.test.js | 72 +-- tests/integration/billing.test.js | 45 +- tests/integration/blockstorage.test.js | 100 ++++ tests/integration/blockstorage.test.ts | 116 ++++ tests/integration/cdn.test.js | 85 +++ tests/integration/cdn.test.ts | 97 ++++ tests/integration/containerRegistry.test.js | 81 +++ tests/integration/containerRegistry.test.ts | 91 +++ tests/integration/databases.test.js | 201 +++++++ tests/integration/databases.test.ts | 232 ++++++++ tests/integration/defaults.js | 6 + tests/integration/defaults.ts | 6 + tests/integration/domain.test.js | 159 +++++ tests/integration/domain.test.ts | 188 ++++++ tests/integration/firewalls.test.js | 193 +++++++ tests/integration/firewalls.test.ts | 252 ++++++++ tests/integration/images.test.js | 116 ++++ tests/integration/images.test.ts | 144 +++++ tests/integration/kubernetes.test.js | 316 ++++++++++ tests/integration/kubernetes.test.ts | 368 ++++++++++++ tests/integration/monitoring.test.js | 528 +++++++++++++++++ tests/integration/monitoring.test.ts | 608 ++++++++++++++++++++ tests/integration/oneClicks.test.js | 87 +++ tests/integration/oneClicks.test.ts | 103 ++++ tests/integration/projects.test.js | 135 +++++ tests/integration/projects.test.ts | 154 +++++ tests/integration/regions.test.js | 18 + tests/integration/regions.test.ts | 23 + tests/integration/reservedIps.test.js | 238 ++++++++ tests/integration/reservedIps.test.ts | 269 +++++++++ tests/integration/shared.js | 53 ++ tests/integration/shared.ts | 69 +++ tests/integration/sizes.test.js | 26 + tests/integration/sizes.test.ts | 32 ++ tests/integration/snapshots.test.js | 68 +++ tests/integration/snapshots.test.ts | 73 +++ tests/integration/sshKeys.test.js | 52 ++ tests/integration/sshKeys.test.ts | 56 ++ tests/integration/tags.test.js | 202 +++++++ tests/integration/tags.test.ts | 238 ++++++++ tests/integration/vpc.test.js | 123 ++++ tests/integration/vpc.test.ts | 139 +++++ tests/mocked/account.test.js | 7 - tests/mocked/account.test.ts | 10 - tests/mocked/app.test.js | 406 +------------ tests/mocked/app.test.ts | 427 +------------- tests/mocked/billling.test.js | 67 --- tests/mocked/billling.test.ts | 76 --- tests/mocked/blockstorage.test.js | 283 +++++++++ tests/mocked/blockstorage.test.ts | 316 ++++++++++ tests/mocked/cdn.test.js | 150 +++++ tests/mocked/cdn.test.ts | 178 ++++++ tests/mocked/containerRegistry.test.js | 489 ++++++++++++++++ tests/mocked/containerRegistry.test.ts | 541 +++++++++++++++++ tests/mocked/databases.test.js | 403 +++++++++++++ tests/mocked/databases.test.ts | 476 +++++++++++++++ tests/mocked/domain.test.js | 406 +++++++++++++ tests/mocked/domain.test.ts | 461 +++++++++++++++ tests/mocked/droplet.test.js | 76 --- tests/mocked/droplet.test.ts | 86 --- tests/mocked/firewalls.test.js | 539 +++++++++++++++++ tests/mocked/firewalls.test.ts | 589 +++++++++++++++++++ tests/mocked/images.test.js | 391 +++++++++++++ tests/mocked/images.test.ts | 418 ++++++++++++++ tests/mocked/monitoring.test.js | 314 ++++++++++ tests/mocked/monitoring.test.ts | 340 +++++++++++ tests/mocked/oneClicks.test.js | 153 +++++ tests/mocked/oneClicks.test.ts | 192 +++++++ tests/mocked/projects.test.js | 481 ++++++++++++++++ tests/mocked/projects.test.ts | 531 +++++++++++++++++ tests/mocked/regions.test.js | 105 ++++ tests/mocked/regions.test.ts | 110 ++++ tests/mocked/reservedIps.test.js | 348 +++++++++++ tests/mocked/reservedIps.test.ts | 369 ++++++++++++ tests/mocked/sizes.test.js | 98 ++++ tests/mocked/sizes.test.ts | 105 ++++ tests/mocked/snapshots.test.js | 114 ++++ tests/mocked/snapshots.test.ts | 127 ++++ tests/mocked/sshKeys.test.js | 141 +++++ tests/mocked/sshKeys.test.ts | 164 ++++++ tests/mocked/tags.test.js | 187 ++++++ tests/mocked/tags.test.ts | 209 +++++++ tests/mocked/vpc.test.js | 258 +++++++++ tests/mocked/vpc.test.ts | 289 ++++++++++ 86 files changed, 16408 insertions(+), 1324 deletions(-) create mode 100644 tests/integration/blockstorage.test.js create mode 100644 tests/integration/blockstorage.test.ts create mode 100644 tests/integration/cdn.test.js create mode 100644 tests/integration/cdn.test.ts create mode 100644 tests/integration/containerRegistry.test.js create mode 100644 tests/integration/containerRegistry.test.ts create mode 100644 tests/integration/databases.test.js create mode 100644 tests/integration/databases.test.ts create mode 100644 tests/integration/defaults.js create mode 100644 tests/integration/defaults.ts create mode 100644 tests/integration/domain.test.js create mode 100644 tests/integration/domain.test.ts create mode 100644 tests/integration/firewalls.test.js create mode 100644 tests/integration/firewalls.test.ts create mode 100644 tests/integration/images.test.js create mode 100644 tests/integration/images.test.ts create mode 100644 tests/integration/kubernetes.test.js create mode 100644 tests/integration/kubernetes.test.ts create mode 100644 tests/integration/monitoring.test.js create mode 100644 tests/integration/monitoring.test.ts create mode 100644 tests/integration/oneClicks.test.js create mode 100644 tests/integration/oneClicks.test.ts create mode 100644 tests/integration/projects.test.js create mode 100644 tests/integration/projects.test.ts create mode 100644 tests/integration/regions.test.js create mode 100644 tests/integration/regions.test.ts create mode 100644 tests/integration/reservedIps.test.js create mode 100644 tests/integration/reservedIps.test.ts create mode 100644 tests/integration/shared.js create mode 100644 tests/integration/shared.ts create mode 100644 tests/integration/sizes.test.js create mode 100644 tests/integration/sizes.test.ts create mode 100644 tests/integration/snapshots.test.js create mode 100644 tests/integration/snapshots.test.ts create mode 100644 tests/integration/sshKeys.test.js create mode 100644 tests/integration/sshKeys.test.ts create mode 100644 tests/integration/tags.test.js create mode 100644 tests/integration/tags.test.ts create mode 100644 tests/integration/vpc.test.js create mode 100644 tests/integration/vpc.test.ts create mode 100644 tests/mocked/blockstorage.test.js create mode 100644 tests/mocked/blockstorage.test.ts create mode 100644 tests/mocked/cdn.test.js create mode 100644 tests/mocked/cdn.test.ts create mode 100644 tests/mocked/containerRegistry.test.js create mode 100644 tests/mocked/containerRegistry.test.ts create mode 100644 tests/mocked/databases.test.js create mode 100644 tests/mocked/databases.test.ts create mode 100644 tests/mocked/domain.test.js create mode 100644 tests/mocked/domain.test.ts create mode 100644 tests/mocked/firewalls.test.js create mode 100644 tests/mocked/firewalls.test.ts create mode 100644 tests/mocked/images.test.js create mode 100644 tests/mocked/images.test.ts create mode 100644 tests/mocked/monitoring.test.js create mode 100644 tests/mocked/monitoring.test.ts create mode 100644 tests/mocked/oneClicks.test.js create mode 100644 tests/mocked/oneClicks.test.ts create mode 100644 tests/mocked/projects.test.js create mode 100644 tests/mocked/projects.test.ts create mode 100644 tests/mocked/regions.test.js create mode 100644 tests/mocked/regions.test.ts create mode 100644 tests/mocked/reservedIps.test.js create mode 100644 tests/mocked/reservedIps.test.ts create mode 100644 tests/mocked/sizes.test.js create mode 100644 tests/mocked/sizes.test.ts create mode 100644 tests/mocked/snapshots.test.js create mode 100644 tests/mocked/snapshots.test.ts create mode 100644 tests/mocked/sshKeys.test.js create mode 100644 tests/mocked/sshKeys.test.ts create mode 100644 tests/mocked/tags.test.js create mode 100644 tests/mocked/tags.test.ts create mode 100644 tests/mocked/vpc.test.js create mode 100644 tests/mocked/vpc.test.ts diff --git a/examples/index.js b/examples/index.js index d5781553b..110d1e316 100644 --- a/examples/index.js +++ b/examples/index.js @@ -3,7 +3,7 @@ import { createDigitalOceanClient } from "../src/dots/digitalOceanClient.js"; import { DigitalOceanApiKeyAuthenticationProvider } from '../src/dots/DigitalOceanApiKeyAuthenticationProvider.js'; import { v4 as uuidv4 } from 'uuid'; import dotenv from 'dotenv'; -dotenv.config(); +dotenv.config({ path: "../.env" }); const token = process.env.DIGITALOCEAN_TOKEN; if (!token) { throw new Error("DIGITALOCEAN_TOKEN not set"); @@ -21,6 +21,8 @@ async function main() { throw new Error("SSH_KEY_NAME not set"); } const sshKey = await findSshKey(keyName); + if (!sshKey.fingerprint) + throw new Error("SSH key fingerprint is undefined or null"); const dropletReq = { name: `test-${uuidv4()}`, region: REGION, @@ -38,7 +40,7 @@ async function main() { filesystemType: "ext4", }; const volume = await createVolume(volumeReq); - console.log("Volume created: ", volume.id); + console.log("Volume created: ", (volume.id)); const volumeActionReq = { dropletId: droplet.id, type: "attach", @@ -68,63 +70,6 @@ async function main() { console.error(err); } } -async function waitForAction(id, wait = 5) { - console.log(`Waiting for action ${id} to complete...`, "", { flush: true }); - let status = "in-progress"; - while (status === "in-progress") { - try { - const resp = await client.v2.actions.byAction_id(id).get(); - if (resp && resp.action) { - status = resp.action.status; - } - else { - throw new Error("Response or action is undefined"); - } - if (status === "in-progress") { - process.stdout.write("."); - await new Promise(resolve => setTimeout(resolve, wait * 1000)); - } - else if (status === "errored") { - throw new Error(`${resp.action.type} action ${resp.action.id} ${status}`); - } - else { - console.log("."); - } - } - catch (err) { - if (err instanceof Error && 'statusCode' in err) { - const httpError = err; - throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); - } - else { - throw err; - } - } - } -} -async function createVolume(req) { - console.log(`Creating volume using: ${JSON.stringify(req)}`); - try { - const resp = await client.v2.volumes.post(req); - if (resp && resp.volume) { - const volume = resp.volume; - console.log(`Created volume ${volume.name} `); - return volume; - } - else { - throw new Error("Failed to create volume or volume is undefined"); - } - } - catch (err) { - if (err instanceof Error && 'statusCode' in err) { - const httpError = err; - throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); - } - else { - throw err; - } - } -} async function findSshKey(name) { console.log(`Looking for SSH key named ${name}...`); let paginated = true; @@ -134,7 +79,6 @@ async function findSshKey(name) { if (resp && resp.sshKeys) { for (const k of resp.sshKeys) { if (k.name === name) { - console.log(`Found SSH key: ${k.fingerprint}`); return k; } } @@ -166,7 +110,7 @@ async function findSshKey(name) { } throw new Error("No SSH key found"); } -async function createDroplet(req = {}) { +async function createDroplet(req) { console.log(`Creating Droplet using: ${JSON.stringify(req)}`); try { const resp = await client.v2.droplets.post(req); @@ -196,7 +140,12 @@ async function createDroplet(req = {}) { } } console.log(`Droplet ID: ${dropletId} Name: ${droplet.name} IP: ${ipAddress}`); - return droplet; + if (droplet?.id == null) { + throw new Error("Droplet ID is null or undefined"); + } + return { + id: droplet.id, + }; } else { throw new Error("Failed to retrieve droplet details or networks information"); @@ -220,4 +169,69 @@ async function createDroplet(req = {}) { } } } +async function createVolume(req) { + console.log(`Creating volume using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.volumes.post(req); + if (resp && resp.volume) { + const volume = resp.volume; + console.log(`Created volume ${volume.name} `); + const volumeData = { + id: volume.id ?? "", + name: volume.name ?? "", + sizeGigabytes: volume.sizeGigabytes ?? 0, + description: volume.description ?? "", + region: String(volume.region) ?? "", + filesystemType: volume.filesystemType ?? "", + }; + return volumeData; + } + else { + throw new Error("Failed to create volume or volume is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } +} +async function waitForAction(id, wait = 5) { + console.log(`Waiting for action ${id} to complete...`, "", { flush: true }); + let status = "in-progress"; + while (status === "in-progress") { + try { + const resp = await client.v2.actions.byAction_id(id).get(); + if (resp && resp.action) { + status = resp.action.status; + } + else { + throw new Error("Response or action is undefined"); + } + if (status === "in-progress") { + process.stdout.write("."); + await new Promise(resolve => setTimeout(resolve, wait * 1000)); + } + else if (status === "errored") { + throw new Error(`${resp.action.type} action ${resp.action.id} ${status}`); + } + else { + console.log("."); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } +} main(); diff --git a/package-lock.json b/package-lock.json index 3526bcb28..889ac888e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "": { "name": "@digitalocean/dots", "version": "1.0.1", - "license": "MIT", + "license": "Apache-2.0", "dependencies": { "@azure/core-http": "^3.0.5", "@microsoft/kiota-authentication-azure": "^1.0.0-preview.61", diff --git a/tests/integration/actions.test.js b/tests/integration/actions.test.js index 388eab544..c9f13a88b 100644 --- a/tests/integration/actions.test.js +++ b/tests/integration/actions.test.js @@ -1,75 +1,17 @@ -import nock from "nock"; import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; -const token = "mock-token"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); const adapter = new FetchRequestAdapter(authProvider); const client = createDigitalOceanClient(adapter); describe("Integration Test for Actions Endpoint", () => { it("should list and get actions", async () => { - // List actions - const expected = { - actions: [ - { - id: 36804636, - status: "completed", - type: "create", - started_at: "2020-11-14T16:29:21Z", - completed_at: "2020-11-14T16:30:06Z", - resource_id: 3164444, - resource_type: "droplet", - region: { - name: "New York 3", - slug: "nyc3", - features: [ - "private_networking", - "backups", - "ipv6", - "metadata", - "install_agent", - "storage", - "image_transfer", - ], - available: true, - sizes: [ - "s-1vcpu-1gb", - "s-1vcpu-2gb", - "s-1vcpu-3gb", - "s-2vcpu-2gb", - "s-3vcpu-1gb", - "s-2vcpu-4gb", - "s-4vcpu-8gb", - "s-6vcpu-16gb", - "s-8vcpu-32gb", - "s-12vcpu-48gb", - "s-16vcpu-64gb", - "s-20vcpu-96gb", - "s-24vcpu-128gb", - "s-32vcpu-192g", - ], - }, - region_slug: "nyc3", - }, - ], - links: { - pages: { - pages: { - first: "https://api.digitalocean.com/v2/account/keys?page=1", - prev: "https://api.digitalocean.com/v2/account/keys?page=2", - }, - }, - }, - meta: { - total: 1, - }, - }; - nock("https://api.digitalocean.com") - .get("/v2/actions") - .reply(200, expected); - nock("https://api.digitalocean.com") - .get(`/v2/actions/${expected.actions[0].id}`) - .reply(200, { action: expected.actions[0] }); const listResp = await client.v2.actions.get(); expect(listResp).not.toBeNull(); expect(listResp?.actions).toBeDefined(); @@ -80,5 +22,5 @@ describe("Integration Test for Actions Endpoint", () => { expect(getResp).not.toBeNull(); expect(getResp?.action).toBeDefined(); expect(getResp?.action?.id).toBe(actionId); - }); + }, 50000); }); diff --git a/tests/integration/billing.test.js b/tests/integration/billing.test.js index dcbc81c39..aa921e082 100644 --- a/tests/integration/billing.test.js +++ b/tests/integration/billing.test.js @@ -1,10 +1,8 @@ -import nock from "nock"; import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; import * as fs from "fs"; -const invoiceUuid = "mock-invoice-uuid"; -const baseUrl = 'https://api.digitalocean.com'; +const invoiceUuid = "something"; import dotenv from "dotenv"; dotenv.config(); const token = process.env.DIGITALOCEAN_TOKEN; @@ -15,28 +13,13 @@ const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); const adapter = new FetchRequestAdapter(authProvider); const client = createDigitalOceanClient(adapter); describe("Integration Test for Billing", () => { - beforeEach(() => { - nock.cleanAll(); - }); it("should get customer balance", async () => { - const expectedResponse = { account_balance: "0.00" }; - nock(baseUrl) - .get("/v2/customers/my/balance") - .reply(200, expectedResponse); const getResp = await client.v2.customers.my.balance.get(); + console.log(getResp); expect(getResp).not.toBeNull(); expect(getResp?.accountBalance).toBe("0.00"); }); it("should list billing history", async () => { - const expectedResponse = { - billing_history: [ - { type: "Invoice" }, - { type: "Payment" }, - ], - }; - nock(baseUrl) - .get("/v2/customers/my/billing_history") - .reply(200, expectedResponse); const getResp = await client.v2.customers.my.billing_history.get(); expect(getResp).not.toBeNull(); expect(getResp?.billingHistory).toBeDefined(); @@ -44,15 +27,6 @@ describe("Integration Test for Billing", () => { getResp?.billingHistory?.[0]?.type === "Payment").toBeTruthy(); }); it("should list invoices", async () => { - const expectedResponse = { - billing_history: [ - { type: "Invoice" }, - { type: "Payment" }, - ], - }; - nock(baseUrl) - .get("/v2/customers/my/billing_history") - .reply(200, expectedResponse); const getResp = await client.v2.customers.my.billing_history.get(); expect(getResp).not.toBeNull(); expect(getResp?.billingHistory).toBeDefined(); @@ -60,20 +34,12 @@ describe("Integration Test for Billing", () => { getResp?.billingHistory?.[0]?.type === "Payment").toBeTruthy(); }); it("should get invoice CSV by UUID", async () => { - const expectedResponse = "product,group_description,"; - nock(baseUrl) - .get(`/v2/customers/my/invoices/${invoiceUuid}/csv`) - .reply(200, expectedResponse); const getResp = await client.v2.customers.my.invoices.byInvoice_uuid(invoiceUuid).csv.get(); expect(getResp).not.toBeNull(); const decodedResp = new TextDecoder('utf-8').decode(getResp); expect(decodedResp).toContain("product,group_description,"); }); it("should get invoice PDF by UUID", async () => { - const expectedResponse = Buffer.from("mock-pdf-content"); - nock(baseUrl) - .get(`/v2/customers/my/invoices/${invoiceUuid}/pdf`) - .reply(200, expectedResponse); const getResp = await client.v2.customers.my.invoices.byInvoice_uuid(invoiceUuid).pdf.get(); expect(getResp).not.toBeNull(); const decodedResp = new TextDecoder('utf-8').decode(getResp); @@ -83,14 +49,7 @@ describe("Integration Test for Billing", () => { fs.unlinkSync(filePath); }); it("should get invoice summary by UUID", async () => { - const expectedResponse = { - user_company: "DigitalOcean", - }; - nock(baseUrl) - .get(`/v2/customers/my/invoices/${invoiceUuid}/summary`) - .reply(200, expectedResponse); const getResp = await client.v2.customers.my.invoices.byInvoice_uuid(invoiceUuid).summary.get(); expect(getResp).not.toBeNull(); - expect(getResp?.userCompany).toBe("DigitalOcean"); }); }); diff --git a/tests/integration/blockstorage.test.js b/tests/integration/blockstorage.test.js new file mode 100644 index 000000000..b5d2473fe --- /dev/null +++ b/tests/integration/blockstorage.test.js @@ -0,0 +1,100 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const REGION = "nyc3"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Block Storage Integration Tests", () => { + it("should test block storage snapshots", async () => { + const volumeReq = { + sizeGigabytes: 10, + name: `test-${uuidv4()}`, + description: "Snapshots testing", + region: REGION, + filesystemType: "ext4", + }; + const volume = await createVolume(volumeReq); + const volumeId = volume.id; + const snapshotName = `test-${uuidv4()}`; + const snapshotResp = await client.v2.volumes.byVolume_id(volumeId).snapshots.post({ name: snapshotName }); + if (!snapshotResp || !snapshotResp.snapshot) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(snapshotResp.snapshot.name).toBe(snapshotName); + const snapshotId = snapshotResp.snapshot.id ?? ''; + const listResp = await client.v2.volumes.byVolume_id(volumeId).snapshots.get(); + if (!listResp || !listResp.snapshots) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(listResp.snapshots.length).toBeGreaterThan(0); + const getResp = await client.v2.volumes.snapshots.bySnapshot_id(snapshotId).get(); + if (!getResp || !getResp.snapshot) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(getResp.snapshot.name).toBe(snapshotName); + const deleteResp = await client.v2.volumes.snapshots.bySnapshot_id(snapshotId).delete(); + expect(deleteResp).toBeUndefined(); + }, 50000); + it("should test block storage", async () => { + const volumeReq = { + sizeGigabytes: 10, + name: `test-${uuidv4()}`, + description: "Block storage testing", + region: REGION, + filesystemType: "ext4", + }; + const volume = await createVolume(volumeReq); + const volumeId = volume.id; + expect(volumeId).toBeDefined(); + const listResp = await client.v2.volumes.get(); + if (!listResp || !listResp.volumes) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(listResp.volumes.length).toBeGreaterThan(0); + const getResp = await client.v2.volumes.byVolume_id(volumeId).get(); + if (!getResp || !getResp.volume) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(getResp.volume.name).toBe(volume.name); + const deleteResp = await client.v2.volumes.byVolume_id(volumeId).delete(); + expect(deleteResp).toBeUndefined(); + }, 50000); + async function createVolume(req) { + console.log(`Creating volume using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.volumes.post(req); + if (resp && resp.volume) { + const volume = resp.volume; + console.log(`Created volume ${volume.name} `); + return { + id: volume.id, + name: volume.name, + sizeGigabytes: volume.sizeGigabytes, + description: volume.description, + region: volume.region, + filesystemType: volume.filesystemType, + }; + } + else { + throw new Error("Failed to create volume or volume is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } +}); diff --git a/tests/integration/blockstorage.test.ts b/tests/integration/blockstorage.test.ts new file mode 100644 index 000000000..6570193f9 --- /dev/null +++ b/tests/integration/blockstorage.test.ts @@ -0,0 +1,116 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import { Volumes_ext4 } from "../../src/dots/models/index.js"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const REGION = "nyc3"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +interface Volume { + id: string; + name: string; + sizeGigabytes: number; + description: string; + region: string; + filesystemType: string; +} + + + +describe("Block Storage Integration Tests", () => { + it("should test block storage snapshots", async () => { + const volumeReq : Volumes_ext4 = { + sizeGigabytes: 10, + name: `test-${uuidv4()}`, + description: "Snapshots testing", + region: REGION, + filesystemType: "ext4", + }; + + const volume: Volume = await createVolume(volumeReq); + const volumeId = volume.id; + const snapshotName = `test-${uuidv4()}`; + const snapshotResp = await client.v2.volumes.byVolume_id(volumeId).snapshots.post({ name: snapshotName }); + if (!snapshotResp || !snapshotResp.snapshot) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(snapshotResp.snapshot.name).toBe(snapshotName); + const snapshotId : string = snapshotResp.snapshot.id ?? ''; + const listResp = await client.v2.volumes.byVolume_id(volumeId).snapshots.get(); + if (!listResp || !listResp.snapshots) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(listResp.snapshots.length).toBeGreaterThan(0); + const getResp = await client.v2.volumes.snapshots.bySnapshot_id(snapshotId).get(); + if (!getResp || !getResp.snapshot) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(getResp.snapshot.name).toBe(snapshotName); + const deleteResp = await client.v2.volumes.snapshots.bySnapshot_id(snapshotId).delete(); + expect(deleteResp).toBeUndefined(); + }, 50000); + + it("should test block storage", async () => { + const volumeReq : Volumes_ext4= { + sizeGigabytes: 10, + name: `test-${uuidv4()}`, + description: "Block storage testing", + region: REGION, + filesystemType: "ext4", + }; + const volume: Volume = await createVolume(volumeReq); + const volumeId = volume.id; + expect(volumeId).toBeDefined(); + const listResp = await client.v2.volumes.get(); + if (!listResp || !listResp.volumes) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(listResp.volumes.length).toBeGreaterThan(0); + const getResp = await client.v2.volumes.byVolume_id(volumeId).get(); + if (!getResp || !getResp.volume) { + throw new Error("Failed to create snapshot or snapshot is undefined"); + } + expect(getResp.volume.name).toBe(volume.name); + const deleteResp = await client.v2.volumes.byVolume_id(volumeId).delete(); + expect(deleteResp).toBeUndefined(); + }, 50000); + +async function createVolume(req: Volumes_ext4): Promise { + console.log(`Creating volume using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.volumes.post(req); + if (resp && resp.volume) { + const volume = resp.volume; + console.log(`Created volume ${volume.name} `); + return { + id: volume.id as string, + name: volume.name as string, + sizeGigabytes: volume.sizeGigabytes as number, + description: volume.description as string, + region: volume.region as string, + filesystemType: volume.filesystemType as string, + }; + } else { + throw new Error("Failed to create volume or volume is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } +} +}); \ No newline at end of file diff --git a/tests/integration/cdn.test.js b/tests/integration/cdn.test.js new file mode 100644 index 000000000..d6faa33f2 --- /dev/null +++ b/tests/integration/cdn.test.js @@ -0,0 +1,85 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("CDN Integration Tests", () => { + it("should test CDN lifecycle", async () => { + const spacesEndpoint = process.env.SPACES_ENDPOINT; + const cdnReq = { + origin: spacesEndpoint, + ttl: 3600 + }; + const cdn = await createCdnEndpoint(cdnReq); + const cdnId = cdn.id; + try { + const listResp = await client.v2.cdn.endpoints.get(); + if (!listResp || !listResp.endpoints) { + throw new Error("Failed to list CDN endpoints"); + } + expect(listResp.endpoints.some(endpoint => endpoint.id === cdnId)).toBe(true); + const getResp = await client.v2.cdn.endpoints.byCdn_id(cdnId).get(); + if (!getResp || !getResp.endpoint) { + throw new Error("Failed to get CDN endpoint"); + } + expect(getResp.endpoint.id).toBe(cdnId); + const newTtl = 86400; + const updateReq = { ttl: newTtl }; + const updateResp = await client.v2.cdn.endpoints.byCdn_id(cdnId).put(updateReq); + if (!updateResp || !updateResp.endpoint) { + throw new Error("Failed to update CDN endpoint"); + } + expect(updateResp.endpoint.ttl).toBe(newTtl); + const purgeReq = { files: ["*"] }; + const purgeResp = await client.v2.cdn.endpoints.byCdn_id(cdnId).cache.delete(purgeReq); + expect(purgeResp).toBeUndefined(); + } + finally { + await deleteCdnEndpoint(cdnId); + } + }, 60000); + async function createCdnEndpoint(req) { + console.log(`Creating CDN endpoint using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.cdn.endpoints.post(req); + if (resp && resp.endpoint) { + const endpoint = resp.endpoint; + console.log(`Created CDN endpoint `); + return { + id: endpoint.id, + origin: endpoint.origin, + ttl: endpoint.ttl, + }; + } + else { + throw new Error("Failed to create CDN endpoint or endpoint is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + async function deleteCdnEndpoint(cdnId) { + console.log(`Deleting CDN endpoint `); + try { + await client.v2.cdn.endpoints.byCdn_id(cdnId).delete(); + console.log(`Deleted CDN endpoint `); + } + catch (err) { + console.error(`Failed to delete CDN endpoint :`, err); + } + } +}); diff --git a/tests/integration/cdn.test.ts b/tests/integration/cdn.test.ts new file mode 100644 index 000000000..486f79872 --- /dev/null +++ b/tests/integration/cdn.test.ts @@ -0,0 +1,97 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Cdn_endpoint } from "../../src/dots/models/index.js"; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +interface CdnEndpoint { + id: string; + origin: string; + ttl: number; +} + +describe("CDN Integration Tests", () => { + it("should test CDN lifecycle", async () => { + const spacesEndpoint = process.env.SPACES_ENDPOINT; + const cdnReq : Cdn_endpoint= { + origin: spacesEndpoint, + ttl: 3600 + }; + const cdn: CdnEndpoint = await createCdnEndpoint(cdnReq); + const cdnId = cdn.id; + + try { + const listResp = await client.v2.cdn.endpoints.get(); + if (!listResp || !listResp.endpoints) { + throw new Error("Failed to list CDN endpoints"); + } + expect(listResp.endpoints.some(endpoint => endpoint.id === cdnId)).toBe(true); + const getResp = await client.v2.cdn.endpoints.byCdn_id(cdnId).get(); + if (!getResp || !getResp.endpoint) { + throw new Error("Failed to get CDN endpoint"); + } + expect(getResp.endpoint.id).toBe(cdnId); + const newTtl = 86400; + const updateReq = { ttl: newTtl }; + const updateResp = await client.v2.cdn.endpoints.byCdn_id(cdnId).put(updateReq); + if (!updateResp || !updateResp.endpoint) { + throw new Error("Failed to update CDN endpoint"); + } + expect(updateResp.endpoint.ttl).toBe(newTtl); + const purgeReq = { files: ["*"] }; + const purgeResp = await client.v2.cdn.endpoints.byCdn_id(cdnId).cache.delete(purgeReq); + expect(purgeResp).toBeUndefined(); + + } finally { + await deleteCdnEndpoint(cdnId); + } + }, 60000); + + async function createCdnEndpoint(req: Cdn_endpoint): Promise { + console.log(`Creating CDN endpoint using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.cdn.endpoints.post(req); + if (resp && resp.endpoint) { + const endpoint = resp.endpoint; + console.log(`Created CDN endpoint `); + return { + id: endpoint.id as string, + origin: endpoint.origin as string, + ttl: endpoint.ttl as number, + }; + } else { + throw new Error("Failed to create CDN endpoint or endpoint is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + async function deleteCdnEndpoint(cdnId: string): Promise { + console.log(`Deleting CDN endpoint `); + try { + await client.v2.cdn.endpoints.byCdn_id(cdnId).delete(); + console.log(`Deleted CDN endpoint `); + } catch (err) { + console.error(`Failed to delete CDN endpoint :`, err); + } + } +}); \ No newline at end of file diff --git a/tests/integration/containerRegistry.test.js b/tests/integration/containerRegistry.test.js new file mode 100644 index 000000000..dfcd7ad92 --- /dev/null +++ b/tests/integration/containerRegistry.test.js @@ -0,0 +1,81 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const PREFIX = "test-dots"; +const REGION = "nyc3"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Container Registry Integration Tests", () => { + it("should test container registry lifecycle", async () => { + const expectedName = `${PREFIX}-${uuidv4()}`; + const containerRegistryReq = { + name: expectedName, + subscriptionTierSlug: "professional", + region: REGION, + }; + try { + const getSubResp = await client.v2.registry.subscription.get(); + if (!getSubResp || !getSubResp.subscription) { + throw new Error("Failed to get subscription"); + } + expect(getSubResp.subscription.tier?.slug).toBe("professional"); + const containerRegistryResp = await client.v2.registries.post(containerRegistryReq); + if (!containerRegistryResp || !containerRegistryResp.registry) { + throw new Error("Failed to create container registry"); + } + expect(containerRegistryResp.registry.name).toBe(expectedName); + const registryName = containerRegistryResp.registry.name; + const getDockerResp = await client.v2.registry.dockerCredentials.get({ + queryParameters: { readWrite: true } + }); + if (!getDockerResp || !getDockerResp.auths) { + throw new Error("Failed to get docker credentials"); + } + expect(getDockerResp.auths["registryDigitaloceanCom"]?.auth).toBeDefined(); + const getResp = await client.v2.registries.byRegistry_name(expectedName).get(); + if (!getResp || !getResp.registry) { + throw new Error("Failed to get registry"); + } + expect(getResp.registry.name).toBe(expectedName); + const newName = `${PREFIX}-${uuidv4()}`; + const validateNameResp = await client.v2.registry.validateName.post({ + name: newName + }); + expect(validateNameResp).toBeUndefined(); + const payLoad = { + type: "unreferenced blobs only", + }; + const garbageResp = await client.v2.registry.byRegistry_name(registryName).garbageCollection.post(payLoad); + if (!garbageResp || !garbageResp.garbageCollection) { + throw new Error("Failed to start garbage collection"); + } + expect(garbageResp.garbageCollection.status).toBe("requested"); + const garbageActiveResp = await client.v2.registry.byRegistry_name(registryName).garbageCollection.get(); + if (!garbageActiveResp || !garbageActiveResp.garbageCollection) { + throw new Error("Failed to get active garbage collection"); + } + expect(garbageActiveResp.garbageCollection.registryName).toBe(registryName); + const garbageListResp = await client.v2.registry.byRegistry_name(registryName).garbageCollections.get(); + if (!garbageListResp || !garbageListResp.garbageCollections) { + throw new Error("Failed to list garbage collections"); + } + expect(garbageListResp.garbageCollections.length).toBeGreaterThan(0); + const registryOptionsResp = await client.v2.registry.optionsPath.get(); + if (!registryOptionsResp || !registryOptionsResp.options) { + throw new Error("Failed to get registry options"); + } + expect(registryOptionsResp.options.availableRegions?.length).toBeGreaterThan(0); + } + catch (error) { + console.error("Failed to cleanup registry:", error); + } + }, 120000); +}); diff --git a/tests/integration/containerRegistry.test.ts b/tests/integration/containerRegistry.test.ts new file mode 100644 index 000000000..75eecdce6 --- /dev/null +++ b/tests/integration/containerRegistry.test.ts @@ -0,0 +1,91 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import { Registry_create } from "../../src/dots/models/index.js"; +import { Registry_run_gc } from "../../src/dots/models/index.js"; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const PREFIX = "test-dots"; +const REGION = "nyc3"; + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Container Registry Integration Tests", () => { + it("should test container registry lifecycle", async () => { + const expectedName = `${PREFIX}-${uuidv4()}`; + const containerRegistryReq: Registry_create = { + name: expectedName, + subscriptionTierSlug: "professional", + region: REGION, + }; + + try { + const getSubResp = await client.v2.registry.subscription.get(); + if (!getSubResp || !getSubResp.subscription) { + throw new Error("Failed to get subscription"); + } + expect(getSubResp.subscription.tier?.slug).toBe("professional"); + + const containerRegistryResp = await client.v2.registries.post(containerRegistryReq); + if (!containerRegistryResp || !containerRegistryResp.registry) { + throw new Error("Failed to create container registry"); + } + expect(containerRegistryResp.registry.name).toBe(expectedName); + const registryName = containerRegistryResp.registry.name as string; + const getDockerResp = await client.v2.registry.dockerCredentials.get({ + queryParameters: { readWrite: true } + }); + if (!getDockerResp || !getDockerResp.auths) { + throw new Error("Failed to get docker credentials"); + } + expect(getDockerResp.auths["registryDigitaloceanCom"]?.auth).toBeDefined(); + const getResp = await client.v2.registries.byRegistry_name(expectedName).get(); + if (!getResp || !getResp.registry) { + throw new Error("Failed to get registry"); + } + expect(getResp.registry.name).toBe(expectedName); + const newName = `${PREFIX}-${uuidv4()}`; + const validateNameResp = await client.v2.registry.validateName.post({ + name: newName + }); + expect(validateNameResp).toBeUndefined(); + const payLoad : Registry_run_gc = { + type: "unreferenced blobs only", + }; + + const garbageResp = await client.v2.registry.byRegistry_name(registryName).garbageCollection.post(payLoad); + if (!garbageResp || !garbageResp.garbageCollection) { + throw new Error("Failed to start garbage collection"); + } + expect(garbageResp.garbageCollection.status).toBe("requested"); + const garbageActiveResp = await client.v2.registry.byRegistry_name(registryName).garbageCollection.get(); + if (!garbageActiveResp || !garbageActiveResp.garbageCollection) { + throw new Error("Failed to get active garbage collection"); + } + expect(garbageActiveResp.garbageCollection.registryName).toBe(registryName); + const garbageListResp = await client.v2.registry.byRegistry_name(registryName).garbageCollections.get(); + if (!garbageListResp || !garbageListResp.garbageCollections) { + throw new Error("Failed to list garbage collections"); + } + expect(garbageListResp.garbageCollections.length).toBeGreaterThan(0); + const registryOptionsResp = await client.v2.registry.optionsPath.get(); + if (!registryOptionsResp || !registryOptionsResp.options) { + throw new Error("Failed to get registry options"); + } + expect(registryOptionsResp.options.availableRegions?.length).toBeGreaterThan(0); + + } catch (error){ + console.error("Failed to cleanup registry:", error); + + } + }, 120000); +}); \ No newline at end of file diff --git a/tests/integration/databases.test.js b/tests/integration/databases.test.js new file mode 100644 index 000000000..ea8cfb139 --- /dev/null +++ b/tests/integration/databases.test.js @@ -0,0 +1,201 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const PREFIX = "test"; +const REGION = "nyc3"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Database Integration Tests", () => { + it("should update connection pool", async () => { + const dbCreateReq = { + name: `${PREFIX}-${uuidv4()}`, + engine: "pg", + version: "14", + region: REGION, + size: "db-s-2vcpu-4gb", + numNodes: 2, + tags: ["production"], + }; + const database = await createDatabase(dbCreateReq); + const dbId = database.id; + try { + await waitForDatabaseActive(dbId); + const createPoolReq = { + name: `${PREFIX}-${uuidv4()}`, + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin", + }; + const poolResp = await client.v2.databases.byDatabase_cluster_uuid(dbId).pools.post(createPoolReq); + if (!poolResp || !poolResp.pool) { + throw new Error("Failed to create connection pool"); + } + expect(poolResp.pool).toBeDefined(); + const poolName = poolResp.pool.name; + const newPoolMode = "session"; + const newPoolSize = 15; + const updatePoolReq = { + mode: newPoolMode, + size: newPoolSize, + db: "defaultdb", + user: "doadmin", + }; + const updatePoolResp = await client.v2.databases.byDatabase_cluster_uuid(dbId).pools.byPool_name(poolName).put(updatePoolReq); + expect(updatePoolResp).toBeUndefined(); + const poolDetails = await client.v2.databases.byDatabase_cluster_uuid(dbId).pools.byPool_name(poolName).get(); + if (!poolDetails || !poolDetails.pool) { + throw new Error("Failed to get connection pool details"); + } + expect(poolDetails.pool.mode).toBe(newPoolMode); + expect(poolDetails.pool.size).toBe(newPoolSize); + } + finally { + await deleteDatabase(dbId); + } + }, 600000); + it("should update major version", async () => { + const dbCreateReq = { + name: `${PREFIX}-${uuidv4()}`, + engine: "pg", + version: "16", + region: REGION, + size: "db-s-2vcpu-4gb", + numNodes: 2, + tags: ["production"], + }; + const database = await createDatabase(dbCreateReq); + const dbId = database.id; + try { + await waitForDatabaseActive(dbId); + const updateReq = { + version: "17", + }; + const updateResp = await client.v2.databases.byDatabase_cluster_uuid(dbId).upgrade.put(updateReq); + expect(updateResp).toBeUndefined(); + } + finally { + await deleteDatabase(dbId); + } + }, 600000); + it("should create replica and promote as primary", async () => { + const dbCreateReq = { + name: `${PREFIX}-${uuidv4()}`, + engine: "pg", + version: "14", + region: REGION, + size: "db-s-2vcpu-4gb", + numNodes: 2, + tags: ["production"], + }; + const database = await createDatabase(dbCreateReq); + const dbId = database.id; + try { + await waitForDatabaseActive(dbId); + const replicaName = "read-dots-nyc3-01"; + const createReplicaReq = { + name: replicaName, + region: REGION, + size: "db-s-2vcpu-4gb", + }; + const createRepResponse = await client.v2.databases.byDatabase_cluster_uuid(dbId).replicas.post(createReplicaReq); + expect(createRepResponse).toBeDefined(); + await waitForReplicaActive(dbId, replicaName); + const promoteReplica = await client.v2.databases.byDatabase_cluster_uuid(dbId).replicas.byReplica_name(replicaName).promote.put(); + expect(promoteReplica).toBeUndefined(); + } + finally { + await deleteDatabase(dbId); + } + }, 1000000); + async function createDatabase(req) { + console.log(`Creating database cluster using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.databases.post(req); + if (resp && resp.database) { + const database = resp.database; + console.log(`Created database cluster ${database.name} `); + return { + id: database.id, + name: database.name, + engine: database.engine, + version: database.version, + region: database.region, + size: database.size, + numNodes: database.numNodes, + status: database.status, + }; + } + else { + throw new Error("Failed to create database cluster or database is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + async function waitForDatabaseActive(dbId, maxWaitTime = 600000) { + const startTime = Date.now(); + const pollInterval = 30000; // 30 seconds + console.log(`Waiting for database ${dbId} to become active...`); + while (Date.now() - startTime < maxWaitTime) { + try { + const resp = await client.v2.databases.byDatabase_cluster_uuid(dbId).get(); + if (resp && resp.database && resp.database.status === "online") { + console.log(`Database ${dbId} is now active`); + return; + } + console.log(`Database ${dbId} status: ${resp?.database?.status}, waiting...`); + } + catch (err) { + console.error(`Error checking database status: ${err}`); + } + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + throw new Error(`Database ${dbId} did not become active within ${maxWaitTime}ms`); + } + async function waitForReplicaActive(dbId, replicaName, maxWaitTime = 600000) { + const startTime = Date.now(); + const pollInterval = 30000; // 30 seconds + console.log(`Waiting for replica ${replicaName} to become active...`); + while (Date.now() - startTime < maxWaitTime) { + try { + const resp = await client.v2.databases.byDatabase_cluster_uuid(dbId).replicas.byReplica_name(replicaName).get(); + if (resp && resp.replica && resp.replica.status === "online") { + console.log(`Replica ${replicaName} is now active`); + return; + } + console.log(`Replica ${replicaName} status: ${resp?.replica?.status}, waiting...`); + } + catch (err) { + console.error(`Error checking replica status: ${err}`); + } + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + throw new Error(`Replica ${replicaName} did not become active within ${maxWaitTime}ms`); + } + async function deleteDatabase(dbId) { + console.log(`Deleting database cluster `); + try { + await client.v2.databases.byDatabase_cluster_uuid(dbId).delete(); + console.log(`Deleted database cluster `); + } + catch (err) { + console.error(`Failed to delete database cluster :`, err); + } + } +}); diff --git a/tests/integration/databases.test.ts b/tests/integration/databases.test.ts new file mode 100644 index 000000000..170d7474f --- /dev/null +++ b/tests/integration/databases.test.ts @@ -0,0 +1,232 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import { DatabasesPostRequestBody} from "../../src/dots/v2/databases/index.js"; +import { Connection_pool , Connection_pool_update, Database_replica} from "../../src/dots/models/index.js"; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const PREFIX = "test"; +const REGION = "nyc3"; + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +interface DatabaseCluster { + id: string; + name: string; + engine: string; + version: string; + region: string; + size: string; + numNodes: number; + status: string; +} + +describe("Database Integration Tests", () => { + + it("should update connection pool", async () => { + const dbCreateReq: DatabasesPostRequestBody = { + name: `${PREFIX}-${uuidv4()}`, + engine: "pg", + version: "14", + region: REGION, + size: "db-s-2vcpu-4gb", + numNodes: 2, + tags: ["production"], + }; + const database: DatabaseCluster = await createDatabase(dbCreateReq); + const dbId = database.id; + try { + await waitForDatabaseActive(dbId); + + const createPoolReq: Connection_pool = { + name: `${PREFIX}-${uuidv4()}`, + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin", + }; + const poolResp = await client.v2.databases.byDatabase_cluster_uuid(dbId).pools.post(createPoolReq); + if (!poolResp || !poolResp.pool) { + throw new Error("Failed to create connection pool"); + } + expect(poolResp.pool).toBeDefined(); + const poolName = poolResp.pool.name as string; + + const newPoolMode = "session"; + const newPoolSize = 15; + + const updatePoolReq: Connection_pool_update = { + mode: newPoolMode, + size: newPoolSize, + db: "defaultdb", + user: "doadmin", + }; + const updatePoolResp = await client.v2.databases.byDatabase_cluster_uuid(dbId).pools.byPool_name(poolName).put(updatePoolReq); + expect(updatePoolResp).toBeUndefined(); + const poolDetails = await client.v2.databases.byDatabase_cluster_uuid(dbId).pools.byPool_name(poolName).get(); + if (!poolDetails || !poolDetails.pool) { + throw new Error("Failed to get connection pool details"); + } + expect(poolDetails.pool.mode).toBe(newPoolMode); + expect(poolDetails.pool.size).toBe(newPoolSize); + + } finally { + await deleteDatabase(dbId); + } + }, 600000); + + it("should update major version", async () => { + const dbCreateReq: DatabasesPostRequestBody = { + name: `${PREFIX}-${uuidv4()}`, + engine: "pg", + version: "16", + region: REGION, + size: "db-s-2vcpu-4gb", + numNodes: 2, + tags: ["production"], + }; + const database: DatabaseCluster = await createDatabase(dbCreateReq); + const dbId = database.id; + try { + await waitForDatabaseActive(dbId); + const updateReq = { + version: "17", + }; + const updateResp = await client.v2.databases.byDatabase_cluster_uuid(dbId).upgrade.put(updateReq); + expect(updateResp).toBeUndefined(); + + } finally { + await deleteDatabase(dbId); + } + }, 600000); + + it("should create replica and promote as primary", async () => { + const dbCreateReq: DatabasesPostRequestBody = { + name: `${PREFIX}-${uuidv4()}`, + engine: "pg", + version: "14", + region: REGION, + size: "db-s-2vcpu-4gb", + numNodes: 2, + tags: ["production"], + }; + const database: DatabaseCluster = await createDatabase(dbCreateReq); + const dbId = database.id; + + try { + await waitForDatabaseActive(dbId); + const replicaName = "read-dots-nyc3-01"; + const createReplicaReq: Database_replica = { + name: replicaName, + region: REGION, + size: "db-s-2vcpu-4gb", + }; + const createRepResponse = await client.v2.databases.byDatabase_cluster_uuid(dbId).replicas.post(createReplicaReq); + expect(createRepResponse).toBeDefined(); + await waitForReplicaActive(dbId, replicaName); + const promoteReplica = await client.v2.databases.byDatabase_cluster_uuid(dbId).replicas.byReplica_name(replicaName).promote.put(); + expect(promoteReplica).toBeUndefined(); + + } finally { + await deleteDatabase(dbId); + } + }, 1000000); + + async function createDatabase(req: DatabasesPostRequestBody): Promise { + console.log(`Creating database cluster using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.databases.post(req); + if (resp && resp.database) { + const database = resp.database; + console.log(`Created database cluster ${database.name} `); + return { + id: database.id as string, + name: database.name as string, + engine: database.engine as string, + version: database.version as string, + region: database.region as string, + size: database.size as string, + numNodes: database.numNodes as number, + status: database.status as string, + }; + } else { + throw new Error("Failed to create database cluster or database is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + async function waitForDatabaseActive(dbId: string, maxWaitTime = 600000): Promise { + const startTime = Date.now(); + const pollInterval = 30000; // 30 seconds + + console.log(`Waiting for database ${dbId} to become active...`); + + while (Date.now() - startTime < maxWaitTime) { + try { + const resp = await client.v2.databases.byDatabase_cluster_uuid(dbId).get(); + if (resp && resp.database && resp.database.status === "online") { + console.log(`Database ${dbId} is now active`); + return; + } + console.log(`Database ${dbId} status: ${resp?.database?.status}, waiting...`); + } catch (err) { + console.error(`Error checking database status: ${err}`); + } + + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + + throw new Error(`Database ${dbId} did not become active within ${maxWaitTime}ms`); + } + async function waitForReplicaActive(dbId: string, replicaName: string, maxWaitTime = 600000): Promise { + const startTime = Date.now(); + const pollInterval = 30000; // 30 seconds + + console.log(`Waiting for replica ${replicaName} to become active...`); + + while (Date.now() - startTime < maxWaitTime) { + try { + const resp = await client.v2.databases.byDatabase_cluster_uuid(dbId).replicas.byReplica_name(replicaName).get(); + if (resp && resp.replica && resp.replica.status === "online") { + console.log(`Replica ${replicaName} is now active`); + return; + } + console.log(`Replica ${replicaName} status: ${resp?.replica?.status}, waiting...`); + } catch (err) { + console.error(`Error checking replica status: ${err}`); + } + + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + + throw new Error(`Replica ${replicaName} did not become active within ${maxWaitTime}ms`); + } + async function deleteDatabase(dbId: string): Promise { + console.log(`Deleting database cluster `); + try { + await client.v2.databases.byDatabase_cluster_uuid(dbId).delete(); + console.log(`Deleted database cluster `); + } catch (err) { + console.error(`Failed to delete database cluster :`, err); + } + } +}); \ No newline at end of file diff --git a/tests/integration/defaults.js b/tests/integration/defaults.js new file mode 100644 index 000000000..9a10991fe --- /dev/null +++ b/tests/integration/defaults.js @@ -0,0 +1,6 @@ +export const defaults = { + PREFIX: process.env.TEST_PREFIX || 'kiota-test', + REGION: process.env.TEST_REGION || 'nyc3', + K8S_VERSION: process.env.K8S_VERSION || '1.33.1-do.2', + K8S_NODE_SIZE: process.env.K8S_NODE_SIZE || 's-2vcpu-2gb' +}; diff --git a/tests/integration/defaults.ts b/tests/integration/defaults.ts new file mode 100644 index 000000000..2cff629d2 --- /dev/null +++ b/tests/integration/defaults.ts @@ -0,0 +1,6 @@ +export const defaults = { + PREFIX: process.env.TEST_PREFIX || 'kiota-test', + REGION: process.env.TEST_REGION || 'nyc3', + K8S_VERSION: process.env.K8S_VERSION || '1.33.1-do.2', + K8S_NODE_SIZE: process.env.K8S_NODE_SIZE || 's-2vcpu-2gb' +}; \ No newline at end of file diff --git a/tests/integration/domain.test.js b/tests/integration/domain.test.js new file mode 100644 index 000000000..04027e280 --- /dev/null +++ b/tests/integration/domain.test.js @@ -0,0 +1,159 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const PREFIX = "test"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Domain Integration Tests", () => { + it("should create record", async () => { + const name = `${PREFIX}${uuidv4()}.com`; + const createDomainReq = { name }; + const domain = await createDomain(createDomainReq); + try { + const listResp = await client.v2.domains.get(); + if (!listResp || !listResp.domains) { + throw new Error("Failed to list domains"); + } + expect(listResp.domains.length).toBeGreaterThan(0); + // Get the specific domain + const getResp = await client.v2.domains.byDomain_name(domain.name).get(); + if (!getResp || !getResp.domain) { + throw new Error("Failed to get domain"); + } + expect(getResp.domain.name).toBe(name); + const createRecordReq = { + type: "A", + name: name, + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }; + const record = await createDomainRecord(domain.name, createRecordReq); + try { + const listRecordsResp = await client.v2.domains.byDomain_name(domain.name).records.get(); + if (!listRecordsResp || !listRecordsResp.domainRecords) { + throw new Error("Failed to list domain records"); + } + expect(listRecordsResp.domainRecords.length).toBeGreaterThan(0); + const getRecordResp = await client.v2.domains.byDomain_name(domain.name).records.byDomain_record_id(record.id).get(); + if (!getRecordResp || !getRecordResp.domainRecord) { + throw new Error("Failed to get domain record"); + } + expect(getRecordResp.domainRecord.id).toBe(record.id); + const newTtl = 900; + const patchRequest = { type: "A", ttl: newTtl }; + const patchResp = await client.v2.domains.byDomain_name(domain.name).records.byDomain_record_id(record.id).patch(patchRequest); + if (!patchResp || !patchResp.domainRecord) { + throw new Error("Failed to patch domain record"); + } + expect(patchResp.domainRecord.ttl).toBe(newTtl); + const updateTtl = 1000; + const updateRequest = { type: "A", ttl: updateTtl }; + const updateResp = await client.v2.domains.byDomain_name(domain.name).records.byDomain_record_id(record.id).put(updateRequest); + if (!updateResp || !updateResp.domainRecord) { + throw new Error("Failed to update domain record"); + } + expect(updateResp.domainRecord.ttl).toBe(updateTtl); + } + finally { + await deleteDomainRecord(domain.name, record.id); + } + } + finally { + await deleteDomain(domain.name); + } + }); + async function createDomain(req) { + console.log(`Creating domain using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.domains.post(req); + if (resp && resp.domain) { + const domain = resp.domain; + console.log(`Created domain ${domain.name}`); + return { + name: domain.name, + ttl: domain.ttl, + zoneFile: domain.zoneFile, + }; + } + else { + throw new Error("Failed to create domain or domain is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + async function createDomainRecord(domainName, req) { + console.log(`Creating domain record for ${domainName} using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.domains.byDomain_name(domainName).records.post(req); + if (resp && resp.domainRecord) { + const record = resp.domainRecord; + console.log(`Created domain record ${record.id} for ${domainName}`); + return { + id: record.id, + type: record.type, + name: record.name, + data: record.data, + priority: record.priority, + port: record.port, + ttl: record.ttl, + weight: record.weight, + flags: record.flags, + tag: record.tag, + }; + } + else { + throw new Error("Failed to create domain record or record is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + async function deleteDomain(domainName) { + console.log(`Deleting domain ${domainName}`); + try { + await client.v2.domains.byDomain_name(domainName).delete(); + console.log(`Deleted domain ${domainName}`); + } + catch (err) { + console.error(`Failed to delete domain ${domainName}:`, err); + } + } + async function deleteDomainRecord(domainName, recordId) { + console.log(`Deleting domain record ${recordId} for ${domainName}`); + try { + await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).delete(); + console.log(`Deleted domain record ${recordId} for ${domainName}`); + } + catch (err) { + console.error(`Failed to delete domain record ${recordId} for ${domainName}:`, err); + } + } +}); diff --git a/tests/integration/domain.test.ts b/tests/integration/domain.test.ts new file mode 100644 index 000000000..18018819f --- /dev/null +++ b/tests/integration/domain.test.ts @@ -0,0 +1,188 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import { Domain } from "../../src/dots/models/index.js"; +import { Domain_record_a } from "../../src/dots/models/index.js"; +import { Domain_record } from "../../src/dots/models/index.js"; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const PREFIX = "test"; + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +interface DomainReq { + name: string; + ttl?: number; + zoneFile?: string; +} + +interface DomainRecord { + id: number; + type: string; + name: string; + data: string; + priority?: number; + port?: number; + ttl: number; + weight?: number; + flags?: number; + tag?: string; +} + +describe("Domain Integration Tests", () => { + it("should create record", async () => { + const name = `${PREFIX}${uuidv4()}.com`; + const createDomainReq: Domain = { name }; + const domain: DomainReq = await createDomain(createDomainReq); + try { + const listResp = await client.v2.domains.get(); + if (!listResp || !listResp.domains) { + throw new Error("Failed to list domains"); + } + expect(listResp.domains.length).toBeGreaterThan(0); + + // Get the specific domain + const getResp = await client.v2.domains.byDomain_name(domain.name).get(); + if (!getResp || !getResp.domain) { + throw new Error("Failed to get domain"); + } + expect(getResp.domain.name).toBe(name); + + const createRecordReq: Domain_record_a = { + type: "A", + name: name, + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }; + const record: DomainRecord = await createDomainRecord(domain.name, createRecordReq); + try { + const listRecordsResp = await client.v2.domains.byDomain_name(domain.name).records.get(); + if (!listRecordsResp || !listRecordsResp.domainRecords) { + throw new Error("Failed to list domain records"); + } + expect(listRecordsResp.domainRecords.length).toBeGreaterThan(0); + const getRecordResp = await client.v2.domains.byDomain_name(domain.name).records.byDomain_record_id(record.id).get(); + if (!getRecordResp || !getRecordResp.domainRecord) { + throw new Error("Failed to get domain record"); + } + expect(getRecordResp.domainRecord.id).toBe(record.id); + const newTtl = 900; + const patchRequest = { type: "A", ttl: newTtl }; + + const patchResp = await client.v2.domains.byDomain_name(domain.name).records.byDomain_record_id(record.id).patch(patchRequest); + if (!patchResp || !patchResp.domainRecord) { + throw new Error("Failed to patch domain record"); + } + expect(patchResp.domainRecord.ttl).toBe(newTtl); + const updateTtl = 1000; + const updateRequest: Domain_record = { type: "A", ttl: updateTtl }; + + const updateResp = await client.v2.domains.byDomain_name(domain.name).records.byDomain_record_id(record.id).put(updateRequest); + if (!updateResp || !updateResp.domainRecord) { + throw new Error("Failed to update domain record"); + } + expect(updateResp.domainRecord.ttl).toBe(updateTtl); + + } finally { + await deleteDomainRecord(domain.name, record.id); + } + + } finally { + await deleteDomain(domain.name); + } + }); + async function createDomain(req: Domain): Promise { + console.log(`Creating domain using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.domains.post(req); + if (resp && resp.domain) { + const domain = resp.domain; + console.log(`Created domain ${domain.name}`); + return { + name: domain.name as string, + ttl: domain.ttl as number, + zoneFile: domain.zoneFile as string, + }; + } else { + throw new Error("Failed to create domain or domain is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + async function createDomainRecord(domainName: string, req: Domain_record_a): Promise { + console.log(`Creating domain record for ${domainName} using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.domains.byDomain_name(domainName).records.post(req); + if (resp && resp.domainRecord) { + const record = resp.domainRecord; + console.log(`Created domain record ${record.id} for ${domainName}`); + return { + id: record.id as number, + type: record.type as string, + name: record.name as string, + data: record.data as string, + priority: record.priority as number, + port: record.port as number, + ttl: record.ttl as number, + weight: record.weight as number, + flags: record.flags as number, + tag: record.tag as string, + }; + } else { + throw new Error("Failed to create domain record or record is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + async function deleteDomain(domainName: string): Promise { + console.log(`Deleting domain ${domainName}`); + try { + await client.v2.domains.byDomain_name(domainName).delete(); + console.log(`Deleted domain ${domainName}`); + } catch (err) { + console.error(`Failed to delete domain ${domainName}:`, err); + } + } + async function deleteDomainRecord(domainName: string, recordId: number): Promise { + console.log(`Deleting domain record ${recordId} for ${domainName}`); + try { + await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).delete(); + console.log(`Deleted domain record ${recordId} for ${domainName}`); + } catch (err) { + console.error(`Failed to delete domain record ${recordId} for ${domainName}:`, err); + } + } +}); \ No newline at end of file diff --git a/tests/integration/firewalls.test.js b/tests/integration/firewalls.test.js new file mode 100644 index 000000000..264df2085 --- /dev/null +++ b/tests/integration/firewalls.test.js @@ -0,0 +1,193 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const PREFIX = "test"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Firewall Integration Tests", () => { + it("should create, update, and delete firewall", async () => { + const tagName = `${PREFIX}-${uuidv4()}`; + const firewallName = `${PREFIX}-${uuidv4()}`; + const tagReq = { name: tagName }; + // Create tag first + await createTag(tagReq); + try { + const createReq = { + name: firewallName, + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + tags: [tagName], + }; + // Create firewall + const firewall = await createFirewall(createReq); + const firewallId = firewall.id; + try { + expect(firewallId).toBeDefined(); + expect(firewall.name).toBe(firewallName); + expect(firewall.tags).toEqual([tagName]); + expect(firewall.outboundRules[0].protocol).toBe("tcp"); + expect(firewall.outboundRules[0].ports).toBe("80"); + expect(firewall.outboundRules[0].destinations).toEqual({ + addresses: ["0.0.0.0/0", "::/0"] + }); + // GET firewall + const getResp = await client.v2.firewalls.byFirewall_id(firewallId).get(); + if (!getResp || !getResp.firewall) { + throw new Error("Failed to get firewall"); + } + const got = getResp.firewall; + expect(got.id).toBe(firewallId); + expect(got.name).toBe(firewallName); + expect(got.tags).toEqual([tagName]); + expect(got.outboundRules?.[0]?.protocol).toBe("tcp"); + expect(got.outboundRules?.[0]?.ports).toBe("80"); + expect(got.outboundRules?.[0]?.destinations).toEqual({ + addresses: ["0.0.0.0/0", "::/0"] + }); + // Add rule + const addRuleReq = { + inboundRules: [ + { + protocol: "tcp", + ports: "2222", + sources: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ] + }; + await client.v2.firewalls.byFirewall_id(firewallId).rules.post(addRuleReq); + const updatedResp = await client.v2.firewalls.byFirewall_id(firewallId).get(); + if (!updatedResp || !updatedResp.firewall) { + throw new Error("Failed to get updated firewall"); + } + const updated = updatedResp.firewall; + expect(updated.inboundRules?.[0]?.protocol).toBe("tcp"); + expect(updated.inboundRules?.[0]?.ports).toBe("2222"); + expect(updated.inboundRules?.[0]?.sources).toEqual({ + addresses: ["0.0.0.0/0", "::/0"] + }); + // Remove rule + const removeRuleReq = { + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ] + }; + await client.v2.firewalls.byFirewall_id(firewallId).rules.delete(removeRuleReq); + const removedResp = await client.v2.firewalls.byFirewall_id(firewallId).get(); + if (!removedResp || !removedResp.firewall) { + throw new Error("Failed to get firewall after rule removal"); + } + const removed = removedResp.firewall; + expect(removed.outboundRules?.length || 0).toBe(0); + } + finally { + // Delete firewall + await deleteFirewall(firewallId); + } + } + finally { + // Delete tag + await deleteTag(tagName); + } + }, 60000); + // Helper function to create a tag + async function createTag(req) { + console.log(`Creating tag using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.tags.post(req); + if (resp && resp.tag) { + const tag = resp.tag; + console.log(`Created tag ${tag.name}`); + return { + name: tag.name, + resources: { + droplets: { + count: tag.resources?.droplets?.count || 0, + lastTagged: tag.resources?.droplets?.lastTaggedUri || "", + } + } + }; + } + else { + throw new Error("Failed to create tag or tag is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + // Helper function to create a firewall + async function createFirewall(req) { + console.log(`Creating firewall using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.firewalls.post(req); + if (resp && resp.firewall) { + const firewall = resp.firewall; + console.log(`Created firewall ${firewall.name} `); + return { + id: firewall.id, + name: firewall.name, + tags: firewall.tags || [], + inboundRules: firewall.inboundRules || [], + outboundRules: firewall.outboundRules || [], + }; + } + else { + throw new Error("Failed to create firewall or firewall is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + // Helper function to delete a firewall + async function deleteFirewall(firewallId) { + console.log(`Deleting firewall `); + try { + await client.v2.firewalls.byFirewall_id(firewallId).delete(); + console.log(`Deleted firewall `); + } + catch (err) { + console.error(`Failed to delete firewall :`, err); + } + } + // Helper function to delete a tag + async function deleteTag(tagName) { + console.log(`Deleting tag ${tagName}`); + try { + await client.v2.tags.byTag_id(tagName).delete(); + console.log(`Deleted tag ${tagName}`); + } + catch (err) { + console.error(`Failed to delete tag ${tagName}:`, err); + } + } +}); diff --git a/tests/integration/firewalls.test.ts b/tests/integration/firewalls.test.ts new file mode 100644 index 000000000..dbf219f5d --- /dev/null +++ b/tests/integration/firewalls.test.ts @@ -0,0 +1,252 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +import { Tags } from "../../src/dots/models/index.js"; +import { Firewall } from "../../src/dots/models/index.js"; +import { Firewall_rules } from "../../src/dots/models/index.js"; +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const PREFIX = "test"; + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +interface FirewallReq { + id: string; + name: string; + tags: string[]; + inboundRules: FirewallRule[]; + outboundRules: FirewallRule[]; +} + +interface FirewallRule { + protocol: string; + ports: string; + sources?: { + addresses?: string[]; + tags?: string[]; + dropletIds?: number[]; + loadBalancerUids?: string[]; + }; + destinations?: { + addresses?: string[]; + tags?: string[]; + dropletIds?: number[]; + loadBalancerUids?: string[]; + }; +} + +interface TagReq { + name: string; + resources: { + droplets: { + count: number; + lastTagged: string; + }; + }; +} + +describe("Firewall Integration Tests", () => { + it("should create, update, and delete firewall", async () => { + const tagName = `${PREFIX}-${uuidv4()}`; + const firewallName = `${PREFIX}-${uuidv4()}`; + + const tagReq: Tags = { name: tagName }; + + // Create tag first + await createTag(tagReq); + + try { + const createReq: Firewall = { + name: firewallName, + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + tags: [tagName], + }; + + // Create firewall + const firewall: FirewallReq = await createFirewall(createReq); + const firewallId = firewall.id; + + try { + expect(firewallId).toBeDefined(); + expect(firewall.name).toBe(firewallName); + expect(firewall.tags).toEqual([tagName]); + expect(firewall.outboundRules[0].protocol).toBe("tcp"); + expect(firewall.outboundRules[0].ports).toBe("80"); + expect(firewall.outboundRules[0].destinations).toEqual({ + addresses: ["0.0.0.0/0", "::/0"] + }); + + // GET firewall + const getResp = await client.v2.firewalls.byFirewall_id(firewallId).get(); + if (!getResp || !getResp.firewall) { + throw new Error("Failed to get firewall"); + } + + const got = getResp.firewall; + expect(got.id).toBe(firewallId); + expect(got.name).toBe(firewallName); + expect(got.tags).toEqual([tagName]); + expect(got.outboundRules?.[0]?.protocol).toBe("tcp"); + expect(got.outboundRules?.[0]?.ports).toBe("80"); + expect(got.outboundRules?.[0]?.destinations).toEqual({ + addresses: ["0.0.0.0/0", "::/0"] + }); + + // Add rule + const addRuleReq: Firewall_rules = { + inboundRules: [ + { + protocol: "tcp", + ports: "2222", + sources: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ] + }; + + await client.v2.firewalls.byFirewall_id(firewallId).rules.post(addRuleReq); + + const updatedResp = await client.v2.firewalls.byFirewall_id(firewallId).get(); + if (!updatedResp || !updatedResp.firewall) { + throw new Error("Failed to get updated firewall"); + } + + const updated = updatedResp.firewall; + expect(updated.inboundRules?.[0]?.protocol).toBe("tcp"); + expect(updated.inboundRules?.[0]?.ports).toBe("2222"); + expect(updated.inboundRules?.[0]?.sources).toEqual({ + addresses: ["0.0.0.0/0", "::/0"] + }); + + // Remove rule + const removeRuleReq: Firewall_rules = { + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ] + }; + + await client.v2.firewalls.byFirewall_id(firewallId).rules.delete(removeRuleReq); + + const removedResp = await client.v2.firewalls.byFirewall_id(firewallId).get(); + if (!removedResp || !removedResp.firewall) { + throw new Error("Failed to get firewall after rule removal"); + } + + const removed = removedResp.firewall; + expect(removed.outboundRules?.length || 0).toBe(0); + + } finally { + // Delete firewall + await deleteFirewall(firewallId); + } + + } finally { + // Delete tag + await deleteTag(tagName); + } + }, 60000); + + // Helper function to create a tag + async function createTag(req: Tags): Promise { + console.log(`Creating tag using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.tags.post(req); + if (resp && resp.tag) { + const tag = resp.tag; + console.log(`Created tag ${tag.name}`); + return { + name: tag.name as string, + resources: { + droplets: { + count: tag.resources?.droplets?.count as number || 0, + lastTagged: tag.resources?.droplets?.lastTaggedUri as string || "", + } + } + }; + } else { + throw new Error("Failed to create tag or tag is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + + // Helper function to create a firewall + async function createFirewall(req: Firewall): Promise { + console.log(`Creating firewall using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.firewalls.post(req); + if (resp && resp.firewall) { + const firewall = resp.firewall; + console.log(`Created firewall ${firewall.name} `); + return { + id: firewall.id as string, + name: firewall.name as string, + tags: firewall.tags as string[] || [], + inboundRules: (firewall.inboundRules as FirewallRule[]) || [], + outboundRules: (firewall.outboundRules as FirewallRule[]) || [], + }; + } else { + throw new Error("Failed to create firewall or firewall is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + + // Helper function to delete a firewall + async function deleteFirewall(firewallId: string): Promise { + console.log(`Deleting firewall `); + try { + await client.v2.firewalls.byFirewall_id(firewallId).delete(); + console.log(`Deleted firewall `); + } catch (err) { + console.error(`Failed to delete firewall :`, err); + } + } + + // Helper function to delete a tag + async function deleteTag(tagName: string): Promise { + console.log(`Deleting tag ${tagName}`); + try { + await client.v2.tags.byTag_id(tagName).delete(); + console.log(`Deleted tag ${tagName}`); + } catch (err) { + console.error(`Failed to delete tag ${tagName}:`, err); + } + } +}); \ No newline at end of file diff --git a/tests/integration/images.test.js b/tests/integration/images.test.js new file mode 100644 index 000000000..edab25321 --- /dev/null +++ b/tests/integration/images.test.js @@ -0,0 +1,116 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const PREFIX = "test"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Image Integration Tests", () => { + it("should create, list, get, update, and delete image", async () => { + const expectedName = `${PREFIX}-${uuidv4()}`; + const imageReq = { + name: expectedName, + url: "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img", + distribution: "Ubuntu", + region: "nyc3", + description: "Cloud-optimized image w/ small footprint", + tags: ["prod"], + }; + // Create custom image + const image = await createCustomImage(imageReq); + const imageId = image?.id; + try { + expect(image.name).toBe(expectedName); + // List all images + const listResp = await client.v2.images.get(); + if (!listResp || !listResp.images) { + throw new Error("Failed to list images"); + } + expect(listResp.images.length).toBeGreaterThan(0); + // List all images with prod tag + const listWithTagResp = await client.v2.images.get({ + queryParameters: { tagName: "prod" } + }); + if (!listWithTagResp || !listWithTagResp.images) { + throw new Error("Failed to list images with tag"); + } + expect(listWithTagResp.images.length).toBeGreaterThan(0); + expect(listWithTagResp.images[0].tags).toContain("prod"); + // Get an image + const getResp = await client.v2.images.byImage_id(imageId).get(); + if (!getResp || !getResp.image) { + throw new Error("Failed to get image"); + } + expect(getResp.image.name).toBe(expectedName); + // Update an image + const newName = `${PREFIX}-${uuidv4()}`; + const updateReq = { + name: newName, + distribution: "Ubuntu", + description: " " + }; + const updateResp = await client.v2.images.byImage_id(imageId).put(updateReq); + if (!updateResp || !updateResp.image) { + throw new Error("Failed to update image"); + } + expect(updateResp.image.name).toBe(newName); + } + finally { + // Delete the image + await deleteImage(imageId); + } + }); + // Helper function to create a custom image + async function createCustomImage(req) { + console.log(`Creating custom image using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.images.post(req); + if (resp && resp.image) { + const image = resp.image; + console.log(`Created custom image ${image.name} `); + return { + id: image.id ?? 0, + name: image.name ?? "", + distribution: image.distribution ?? "", + description: image.description ?? "", + createdAt: image.createdAt ? new Date(image.createdAt) : new Date(), + errorMessage: image.errorMessage ?? "", // Can be undefined + regions: image.regions?.map((r) => String(r)) ?? [], + type: image.type ?? "", // Can be undefined + tags: image.tags ?? [], + status: image.status ?? "", + }; + } + else { + throw new Error("Failed to create custom image or image is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + // Helper function to delete an image + async function deleteImage(imageId) { + console.log(`Deleting image `); + try { + await client.v2.images.byImage_id(imageId).delete(); + console.log(`Deleted image `); + } + catch (err) { + console.error(`Failed to delete image :`, err); + } + } +}); diff --git a/tests/integration/images.test.ts b/tests/integration/images.test.ts new file mode 100644 index 000000000..aacb9d2b2 --- /dev/null +++ b/tests/integration/images.test.ts @@ -0,0 +1,144 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import { Image_new_custom } from "../../src/dots/models/index.js"; +import { Image_update } from "../../src/dots/models/index.js"; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const PREFIX = "test"; + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +interface Image { + id: number; + name: string; + distribution: string; + description: string; + createdAt: Date; + errorMessage: string; + regions: string[]; + type: string; + tags: string[]; + status: string; +} + +describe("Image Integration Tests", () => { + it("should create, list, get, update, and delete image", async () => { + const expectedName = `${PREFIX}-${uuidv4()}`; + const imageReq: Image_new_custom = { + name: expectedName, + url: "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img", + distribution: "Ubuntu", + region: "nyc3", + description: "Cloud-optimized image w/ small footprint", + tags: ["prod"], + }; + + // Create custom image + const image = await createCustomImage(imageReq); + const imageId = image?.id; + + try { + expect(image.name).toBe(expectedName); + + // List all images + const listResp = await client.v2.images.get(); + if (!listResp || !listResp.images) { + throw new Error("Failed to list images"); + } + expect(listResp.images.length).toBeGreaterThan(0); + + // List all images with prod tag + const listWithTagResp = await client.v2.images.get({ + queryParameters: { tagName: "prod" } + }); + if (!listWithTagResp || !listWithTagResp.images) { + throw new Error("Failed to list images with tag"); + } + expect(listWithTagResp.images.length).toBeGreaterThan(0); + expect(listWithTagResp.images[0].tags).toContain("prod"); + + // Get an image + const getResp = await client.v2.images.byImage_id(imageId).get(); + if (!getResp || !getResp.image) { + throw new Error("Failed to get image"); + } + expect(getResp.image.name).toBe(expectedName); + + // Update an image + const newName = `${PREFIX}-${uuidv4()}`; + const updateReq: Image_update = { + name: newName, + distribution: "Ubuntu", + description: " " + }; + + const updateResp = await client.v2.images.byImage_id(imageId).put(updateReq); + if (!updateResp || !updateResp.image) { + throw new Error("Failed to update image"); + } + expect(updateResp.image.name).toBe(newName); + + } finally { + // Delete the image + await deleteImage(imageId); + } + }); + + // Helper function to create a custom image + async function createCustomImage(req: Image_new_custom): Promise { + console.log(`Creating custom image using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.images.post(req); + if (resp && resp.image) { + const image = resp.image; + console.log(`Created custom image ${image.name} `); + return { + id: image.id ?? 0, + name: image.name ?? "", + distribution: image.distribution ?? "", + description: image.description ?? "", + createdAt: image.createdAt ? new Date(image.createdAt) : new Date(), + errorMessage: image.errorMessage ?? "", // Can be undefined + regions: image.regions?.map((r) => String(r)) ?? [], + type: image.type ?? "", // Can be undefined + tags: image.tags ?? [], + status: image.status ?? "", + }; + } else { + throw new Error("Failed to create custom image or image is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + + // Helper function to delete an image + async function deleteImage(imageId: number): Promise { + console.log(`Deleting image `); + try { + await client.v2.images.byImage_id(imageId).delete(); + console.log(`Deleted image `); + } catch (err) { + console.error(`Failed to delete image :`, err); + } + } +}); \ No newline at end of file diff --git a/tests/integration/kubernetes.test.js b/tests/integration/kubernetes.test.js new file mode 100644 index 000000000..120dbc066 --- /dev/null +++ b/tests/integration/kubernetes.test.js @@ -0,0 +1,316 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const PREFIX = "test"; +const REGION = "nyc3"; +const K8S_VERSION = "1.33.1-do.2"; +const K8S_NODE_SIZE = "s-2vcpu-2gb"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +const CLUSTER_CREATE_BASIC_REQ = { + name: `${PREFIX}-cluster-${uuidv4()}`, + region: REGION, + version: K8S_VERSION, + nodePools: [{ size: K8S_NODE_SIZE, count: 2, name: "workers" }], +}; +// Helper to get existing cluster ID from environment +const getExistingClusterId = () => { + return process.env.DO_EXISTING_CLUSTER_ID || ""; +}; +describe("Kubernetes Integration Tests", () => { + it("should test kubernetes operations", async () => { + const existingClusterId = getExistingClusterId(); + const createReq = CLUSTER_CREATE_BASIC_REQ; + // Create cluster and test operations + const cluster = await createKubernetesCluster(createReq, existingClusterId); + const clusterId = cluster.id; + try { + expect(clusterId).toBeTruthy(); + // List clusters + const listResp = await client.v2.kubernetes.clusters.get(); + if (!listResp || !listResp.kubernetesClusters) { + throw new Error("Failed to list clusters"); + } + expect(listResp.kubernetesClusters).toBeDefined(); + // List options + const optsResp = await client.v2.kubernetes.optionsPath.get(); + if (!optsResp || !optsResp.options) { + throw new Error("Failed to get options"); + } + const options = Object.keys(optsResp.options); + expect(options).toEqual(expect.arrayContaining(["regions", "versions", "sizes"])); + // Add registry + const addRegReq = { clusterUuids: [clusterId] }; + const addRegResp = await client.v2.kubernetes.registry.post(addRegReq); + expect(addRegResp).toBeUndefined(); + // Remove registry + const removeRegReq = { clusterUuids: [clusterId] }; + const removeRegResp = await client.v2.kubernetes.registry.delete(removeRegReq); + expect(removeRegResp).toBeUndefined(); + } + finally { + // Clean up cluster + await deleteKubernetesCluster(clusterId); + } + }, 600000); + it("should test kubernetes cluster operations", async () => { + const existingClusterId = getExistingClusterId(); + const createReq = CLUSTER_CREATE_BASIC_REQ; + // Create cluster and test operations + const cluster = await createKubernetesCluster(createReq, existingClusterId); + const clusterId = cluster.id; + const nodePoolId = cluster.nodePools[0].id; + try { + expect(clusterId).toBeTruthy(); + expect(nodePoolId).toBeTruthy(); + const getResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).get(); + if (!getResp || !getResp.kubernetesCluster) { + throw new Error("Failed to get cluster"); + } + expect(getResp.kubernetesCluster.status?.state).toBe("running"); + const updatedClusterName = `cg-updated-${uuidv4()}`; + const updatedTags = ["k8s", `k8s:${clusterId}`, "client-gen"]; + const updateReq = { + name: updatedClusterName, + tags: updatedTags, + }; + const updateResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).put(updateReq); + if (!updateResp || !updateResp.kubernetesCluster) { + throw new Error("Failed to update cluster"); + } + expect(updateResp.kubernetesCluster.name).toBe(updatedClusterName); + expect(updateResp.kubernetesCluster.tags).toEqual(expect.arrayContaining(updatedTags)); + const credResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).credentials.get({ + queryParameters: { expirySeconds: 1 } + }); + if (!credResp) { + throw new Error("Failed to get credentials"); + } + expect(credResp.server).toContain(clusterId); + const assocResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).destroy_with_associated_resources.get(); + if (!assocResp) { + throw new Error("Failed to list associated resources"); + } + expect(assocResp.loadBalancers).toBeDefined(); + expect(assocResp.volumes).toBeDefined(); + expect(assocResp.volumeSnapshots).toBeDefined(); + try { + await client.v2.kubernetes.clusters.byCluster_id(clusterId).destroy_with_associated_resources.dangerous.delete(); + throw new Error("Should have thrown an error for invalid resource"); + } + catch (err) { + expect(err).toBeDefined(); // Expect this to fail + } + const userResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).user.get(); + if (!userResp || !userResp.kubernetesClusterUser) { + throw new Error("Failed to get cluster user"); + } + expect(userResp.kubernetesClusterUser.username).toBeTruthy(); + const clusterLint = { + includeGroups: ["basic", "doks", "security"], + includeChecks: ["bare-pods", "resource-requirements"], + excludeGroups: ["workload-health"], + excludeChecks: ["default-namespace"], + }; + const runLintResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).clusterlint.post(clusterLint); + if (!runLintResp) { + throw new Error("Failed to run clusterlint"); + } + const lintRunId = runLintResp.runId; + expect(lintRunId).toBeTruthy(); + await new Promise(resolve => setTimeout(resolve, 15000)); + const getLintResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).clusterlint.get(); + if (!getLintResp) { + throw new Error("Failed to get clusterlint results"); + } + expect(getLintResp.runId).toBe(lintRunId); + expect(Array.isArray(getLintResp.diagnostics)).toBe(true); + } + catch (err) { + console.log(err); + } + }, 600000); + it("should test kubernetes node pool operations", async () => { + const existingClusterId = getExistingClusterId(); + const nodePoolName = "workers"; + const createReq = { + name: `${PREFIX}-cluster-${uuidv4()}`, + region: REGION, + version: K8S_VERSION, + nodePools: [{ size: K8S_NODE_SIZE, count: 2, name: nodePoolName }], + }; + const cluster = await createKubernetesCluster(createReq, existingClusterId); + const clusterId = cluster.id; + const nodePoolId = cluster.nodePools[0].id; + try { + expect(clusterId).toBeTruthy(); + expect(nodePoolId).toBeTruthy(); + const listResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.get(); + if (!listResp || !listResp.nodePools) { + throw new Error("Failed to list node pools"); + } + const poolNames = listResp.nodePools.map(pool => pool.name); + expect(poolNames).toContain(nodePoolName); + const newPoolName = "new-pool"; + const addPoolReq = { + size: "s-1vcpu-2gb", + count: 3, + name: newPoolName, + tags: ["frontend"], + autoScale: true, + minNodes: 3, + maxNodes: 6, + }; + const addPoolResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.post(addPoolReq); + if (!addPoolResp || !addPoolResp.nodePool) { + throw new Error("Failed to add node pool"); + } + expect(addPoolResp.nodePool.name).toBe(newPoolName); + const newPoolId = addPoolResp.nodePool.id; + const getPoolResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.byNode_pool_id(newPoolId).get(); + if (!getPoolResp || !getPoolResp.nodePool) { + throw new Error("Failed to get node pool"); + } + expect(getPoolResp.nodePool.id).toBe(newPoolId); + const updatedPoolName = `${newPoolName}-${uuidv4()}`; + const updatePoolReq = { + name: updatedPoolName, + count: 4 + }; + const updatePoolResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.byNode_pool_id(newPoolId).put(updatePoolReq); + if (!updatePoolResp || !updatePoolResp.nodePool) { + throw new Error("Failed to update node pool"); + } + expect(updatePoolResp.nodePool.name).toBe(updatedPoolName); + if (updatePoolResp.nodePool.nodes && updatePoolResp.nodePool.nodes.length > 0) { + const nodeId = updatePoolResp.nodePool.nodes[0].id; + const deleteNodeResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.byNode_pool_id(newPoolId).nodes.byNode_id(nodeId).delete(); + expect(deleteNodeResp).toBeUndefined(); + } + const deletePoolResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.byNode_pool_id(newPoolId).delete(); + expect(deletePoolResp).toBeUndefined(); + } + finally { + await deleteKubernetesCluster(clusterId); + } + }, 600000); + it("should test kubernetes cluster upgrade", async () => { + const existingClusterId = getExistingClusterId(); + const optsResp = await client.v2.kubernetes.optionsPath.get(); + if (!optsResp || !optsResp.options || !optsResp.options.versions) { + throw new Error("Failed to get kubernetes options"); + } + const versions = optsResp.options.versions; + const slugs = versions.map(v => v.slug).filter(Boolean).sort(); + expect(slugs.length).toBeGreaterThan(0); + const minVersion = slugs[0]; + const createReq = { + name: `${PREFIX}-cluster-${uuidv4()}`, + region: REGION, + version: minVersion, + nodePools: [{ size: K8S_NODE_SIZE, count: 2, name: "workers" }], + }; + const cluster = await createKubernetesCluster(createReq, existingClusterId); + const clusterId = cluster.id; + try { + const upgradesResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).upgrades.get(); + if (!upgradesResp || !upgradesResp.availableUpgradeVersions) { + throw new Error("Failed to get available upgrades"); + } + expect(upgradesResp.availableUpgradeVersions.length).toBeGreaterThanOrEqual(1); + const nextVersion = upgradesResp.availableUpgradeVersions[0].slug; + if (!nextVersion) { + throw new Error("No upgrade version available"); + } + const upgradeReq = { version: nextVersion }; + const upgradeResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).upgrade.post(upgradeReq); + expect(upgradeResp).toBeUndefined(); + } + finally { + await deleteKubernetesCluster(clusterId); + } + }, 600000); + async function createKubernetesCluster(req, existingClusterId) { + if (existingClusterId) { + console.log(`Using existing cluster ID: ${existingClusterId}`); + const resp = await client.v2.kubernetes.clusters.byCluster_id(existingClusterId).get(); + if (!resp || !resp.kubernetesCluster) { + throw new Error("Failed to get existing cluster"); + } + return mapToKubernetesCluster(resp.kubernetesCluster); + } + console.log(`Creating kubernetes cluster using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.kubernetes.clusters.post(req); + if (resp && resp.kubernetesCluster) { + const cluster = resp.kubernetesCluster; + console.log(`Created kubernetes cluster ${cluster.name} `); + await waitForClusterReady(cluster.id); + return mapToKubernetesCluster(cluster); + } + else { + throw new Error("Failed to create kubernetes cluster or cluster is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + async function waitForClusterReady(clusterId, maxWaitTime = 600000) { + const startTime = Date.now(); + while (Date.now() - startTime < maxWaitTime) { + try { + const resp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).get(); + if (resp?.kubernetesCluster?.status?.state === "running") { + console.log(`Cluster ${clusterId} is ready`); + return; + } + console.log(`Waiting for cluster ${clusterId} to be ready...`); + await new Promise(resolve => setTimeout(resolve, 30000)); // Wait 30 seconds + } + catch (err) { + console.error(`Error checking cluster status: ${err}`); + await new Promise(resolve => setTimeout(resolve, 30000)); + } + } + throw new Error(`Cluster ${clusterId} did not become ready within ${maxWaitTime}ms`); + } + function mapToKubernetesCluster(cluster) { + return { + id: String(cluster.id || ""), + name: cluster.name || "", + region: cluster.region || "", + version: cluster.version || "", + nodePools: (cluster.nodePools || []), + status: { + state: cluster.status?.state || "" + }, + tags: cluster.tags || [], + createdAt: cluster.createdAt ? new Date(cluster.createdAt) : new Date(), + }; + } + async function deleteKubernetesCluster(clusterId) { + console.log(`Deleting kubernetes cluster `); + try { + await client.v2.kubernetes.clusters.byCluster_id(clusterId).delete(); + console.log(`Deleted kubernetes cluster `); + } + catch (err) { + console.error(`Failed to delete kubernetes cluster :`, err); + } + } +}); diff --git a/tests/integration/kubernetes.test.ts b/tests/integration/kubernetes.test.ts new file mode 100644 index 000000000..4227c8bce --- /dev/null +++ b/tests/integration/kubernetes.test.ts @@ -0,0 +1,368 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +import { Cluster, Cluster_read } from "../../src/dots/models/index.js"; +import { Cluster_registries } from "../../src/dots/models/index.js"; +import { Cluster_update } from "../../src/dots/models/index.js"; +import { Clusterlint_request } from "../../src/dots/models/index.js"; +import { Kubernetes_node_pool, Kubernetes_node_pool_update } from "../../src/dots/models/index.js"; +import { UpgradePostRequestBody } from "../../src/dots/v2/kubernetes/clusters/item/upgrade/index.js"; +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const PREFIX = "test"; +const REGION = "nyc3"; +const K8S_VERSION = "1.33.1-do.2"; +const K8S_NODE_SIZE = "s-2vcpu-2gb"; + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +interface KubernetesCluster { + id: string; + name: string; + region: string; + version: string; + nodePools: Kubernetes_node_pool[]; + status: { + state: string; + }; + tags: string[]; + createdAt: Date; +} + +const CLUSTER_CREATE_BASIC_REQ: Cluster = { + name: `${PREFIX}-cluster-${uuidv4()}`, + region: REGION, + version: K8S_VERSION, + nodePools: [{ size: K8S_NODE_SIZE, count: 2, name: "workers" }], +}; + +// Helper to get existing cluster ID from environment +const getExistingClusterId = (): string => { + return process.env.DO_EXISTING_CLUSTER_ID || ""; +}; + +describe("Kubernetes Integration Tests", () => { + it("should test kubernetes operations", async () => { + const existingClusterId = getExistingClusterId(); + const createReq = CLUSTER_CREATE_BASIC_REQ; + + // Create cluster and test operations + const cluster = await createKubernetesCluster(createReq, existingClusterId); + const clusterId = cluster.id; + + try { + expect(clusterId).toBeTruthy(); + + // List clusters + const listResp = await client.v2.kubernetes.clusters.get(); + if (!listResp || !listResp.kubernetesClusters) { + throw new Error("Failed to list clusters"); + } + expect(listResp.kubernetesClusters).toBeDefined(); + + // List options + const optsResp = await client.v2.kubernetes.optionsPath.get(); + if (!optsResp || !optsResp.options) { + throw new Error("Failed to get options"); + } + const options = Object.keys(optsResp.options); + expect(options).toEqual(expect.arrayContaining(["regions", "versions", "sizes"])); + + // Add registry + const addRegReq: Cluster_registries = { clusterUuids: [clusterId] }; + const addRegResp = await client.v2.kubernetes.registry.post(addRegReq); + expect(addRegResp).toBeUndefined(); + + // Remove registry + const removeRegReq: Cluster_registries = { clusterUuids: [clusterId] }; + const removeRegResp = await client.v2.kubernetes.registry.delete(removeRegReq); + expect(removeRegResp).toBeUndefined(); + + } finally { + // Clean up cluster + await deleteKubernetesCluster(clusterId); + } + }, 600000); + + it("should test kubernetes cluster operations", async () => { + const existingClusterId = getExistingClusterId(); + const createReq = CLUSTER_CREATE_BASIC_REQ; + + // Create cluster and test operations + const cluster = await createKubernetesCluster(createReq, existingClusterId); + const clusterId = cluster.id; + const nodePoolId = cluster.nodePools[0].id; + + try { + expect(clusterId).toBeTruthy(); + expect(nodePoolId).toBeTruthy(); + const getResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).get(); + if (!getResp || !getResp.kubernetesCluster) { + throw new Error("Failed to get cluster"); + } + expect(getResp.kubernetesCluster.status?.state).toBe("running"); + const updatedClusterName = `cg-updated-${uuidv4()}`; + const updatedTags = ["k8s", `k8s:${clusterId}`, "client-gen"]; + const updateReq: Cluster_update = { + name: updatedClusterName, + tags: updatedTags, + }; + + const updateResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).put(updateReq); + if (!updateResp || !updateResp.kubernetesCluster) { + throw new Error("Failed to update cluster"); + } + expect(updateResp.kubernetesCluster.name).toBe(updatedClusterName); + expect(updateResp.kubernetesCluster.tags).toEqual(expect.arrayContaining(updatedTags)); + const credResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).credentials.get({ + queryParameters: { expirySeconds: 1 } + }); + if (!credResp) { + throw new Error("Failed to get credentials"); + } + expect(credResp.server).toContain(clusterId); + const assocResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).destroy_with_associated_resources.get(); + if (!assocResp) { + throw new Error("Failed to list associated resources"); + } + expect(assocResp.loadBalancers).toBeDefined(); + expect(assocResp.volumes).toBeDefined(); + expect(assocResp.volumeSnapshots).toBeDefined(); + try { + await client.v2.kubernetes.clusters.byCluster_id(clusterId).destroy_with_associated_resources.dangerous.delete(); + throw new Error("Should have thrown an error for invalid resource"); + } catch (err) { + expect(err).toBeDefined(); // Expect this to fail + } + const userResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).user.get(); + if (!userResp || !userResp.kubernetesClusterUser) { + throw new Error("Failed to get cluster user"); + } + expect(userResp.kubernetesClusterUser.username).toBeTruthy(); + const clusterLint : Clusterlint_request = { + includeGroups: ["basic", "doks", "security"], + includeChecks: ["bare-pods", "resource-requirements"], + excludeGroups: ["workload-health"], + excludeChecks: ["default-namespace"], + }; + const runLintResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).clusterlint.post(clusterLint); + if (!runLintResp) { + throw new Error("Failed to run clusterlint"); + } + const lintRunId = runLintResp.runId; + expect(lintRunId).toBeTruthy(); + await new Promise(resolve => setTimeout(resolve, 15000)); + const getLintResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).clusterlint.get(); + if (!getLintResp) { + throw new Error("Failed to get clusterlint results"); + } + expect(getLintResp.runId).toBe(lintRunId); + expect(Array.isArray(getLintResp.diagnostics)).toBe(true); + + } catch (err){ + console.log(err); + } + }, 600000); + + it("should test kubernetes node pool operations", async () => { + const existingClusterId = getExistingClusterId(); + const nodePoolName = "workers"; + + const createReq: Cluster = { + name: `${PREFIX}-cluster-${uuidv4()}`, + region: REGION, + version: K8S_VERSION, + nodePools: [{ size: K8S_NODE_SIZE, count: 2, name: nodePoolName }], + }; + const cluster = await createKubernetesCluster(createReq, existingClusterId); + const clusterId = cluster.id; + const nodePoolId = cluster.nodePools[0].id; + + try { + expect(clusterId).toBeTruthy(); + expect(nodePoolId).toBeTruthy(); + const listResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.get(); + if (!listResp || !listResp.nodePools) { + throw new Error("Failed to list node pools"); + } + const poolNames = listResp.nodePools.map(pool => pool.name); + expect(poolNames).toContain(nodePoolName); + const newPoolName = "new-pool"; + const addPoolReq: Kubernetes_node_pool = { + size: "s-1vcpu-2gb", + count: 3, + name: newPoolName, + tags: ["frontend"], + autoScale: true, + minNodes: 3, + maxNodes: 6, + }; + + const addPoolResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.post(addPoolReq); + if (!addPoolResp || !addPoolResp.nodePool) { + throw new Error("Failed to add node pool"); + } + expect(addPoolResp.nodePool.name).toBe(newPoolName); + const newPoolId = addPoolResp.nodePool.id!; + const getPoolResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.byNode_pool_id(newPoolId).get(); + if (!getPoolResp || !getPoolResp.nodePool) { + throw new Error("Failed to get node pool"); + } + expect(getPoolResp.nodePool.id).toBe(newPoolId); + const updatedPoolName = `${newPoolName}-${uuidv4()}`; + const updatePoolReq: Kubernetes_node_pool_update = { + name: updatedPoolName, + count: 4 + }; + + const updatePoolResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.byNode_pool_id(newPoolId).put(updatePoolReq); + if (!updatePoolResp || !updatePoolResp.nodePool) { + throw new Error("Failed to update node pool"); + } + expect(updatePoolResp.nodePool.name).toBe(updatedPoolName); + + if (updatePoolResp.nodePool.nodes && updatePoolResp.nodePool.nodes.length > 0) { + const nodeId = updatePoolResp.nodePool.nodes[0].id!; + const deleteNodeResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.byNode_pool_id(newPoolId).nodes.byNode_id(nodeId).delete(); + expect(deleteNodeResp).toBeUndefined(); + } + const deletePoolResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).node_pools.byNode_pool_id(newPoolId).delete(); + expect(deletePoolResp).toBeUndefined(); + + } finally { + await deleteKubernetesCluster(clusterId); + } + }, 600000); + + it("should test kubernetes cluster upgrade", async () => { + const existingClusterId = getExistingClusterId(); + const optsResp = await client.v2.kubernetes.optionsPath.get(); + if (!optsResp || !optsResp.options || !optsResp.options.versions) { + throw new Error("Failed to get kubernetes options"); + } + + const versions = optsResp.options.versions; + const slugs = versions.map(v => v.slug).filter(Boolean).sort(); + expect(slugs.length).toBeGreaterThan(0); + + const minVersion = slugs[0]; + + const createReq: Cluster = { + name: `${PREFIX}-cluster-${uuidv4()}`, + region: REGION, + version: minVersion, + nodePools: [{ size: K8S_NODE_SIZE, count: 2, name: "workers" }], + }; + + const cluster = await createKubernetesCluster(createReq, existingClusterId); + const clusterId = cluster.id; + + try { + const upgradesResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).upgrades.get(); + if (!upgradesResp || !upgradesResp.availableUpgradeVersions) { + throw new Error("Failed to get available upgrades"); + } + expect(upgradesResp.availableUpgradeVersions.length).toBeGreaterThanOrEqual(1); + + const nextVersion = upgradesResp.availableUpgradeVersions[0].slug; + if (!nextVersion) { + throw new Error("No upgrade version available"); + } + + const upgradeReq: UpgradePostRequestBody = { version: nextVersion }; + const upgradeResp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).upgrade.post(upgradeReq); + expect(upgradeResp).toBeUndefined(); + + } finally { + await deleteKubernetesCluster(clusterId); + } + }, 600000); + async function createKubernetesCluster(req: Cluster, existingClusterId?: string): Promise { + if (existingClusterId) { + console.log(`Using existing cluster ID: ${existingClusterId}`); + const resp = await client.v2.kubernetes.clusters.byCluster_id(existingClusterId).get(); + if (!resp || !resp.kubernetesCluster) { + throw new Error("Failed to get existing cluster"); + } + return mapToKubernetesCluster(resp.kubernetesCluster); + } + + console.log(`Creating kubernetes cluster using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.kubernetes.clusters.post(req); + if (resp && resp.kubernetesCluster) { + const cluster = resp.kubernetesCluster; + console.log(`Created kubernetes cluster ${cluster.name} `); + + await waitForClusterReady(cluster.id as string); + + return mapToKubernetesCluster(cluster); + } else { + throw new Error("Failed to create kubernetes cluster or cluster is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + + async function waitForClusterReady(clusterId: string, maxWaitTime = 600000): Promise { + const startTime = Date.now(); + + while (Date.now() - startTime < maxWaitTime) { + try { + const resp = await client.v2.kubernetes.clusters.byCluster_id(clusterId).get(); + if (resp?.kubernetesCluster?.status?.state === "running") { + console.log(`Cluster ${clusterId} is ready`); + return; + } + console.log(`Waiting for cluster ${clusterId} to be ready...`); + await new Promise(resolve => setTimeout(resolve, 30000)); // Wait 30 seconds + } catch (err) { + console.error(`Error checking cluster status: ${err}`); + await new Promise(resolve => setTimeout(resolve, 30000)); + } + } + + throw new Error(`Cluster ${clusterId} did not become ready within ${maxWaitTime}ms`); + } +function mapToKubernetesCluster(cluster: Cluster_read): KubernetesCluster { + return { + id: String(cluster.id || ""), + name: cluster.name || "", + region: cluster.region || "", + version: cluster.version || "", + nodePools: (cluster.nodePools || []), + status: { + state: cluster.status?.state || "" + }, + tags: cluster.tags || [], + createdAt: cluster.createdAt ? new Date(cluster.createdAt) : new Date(), + }; +} + async function deleteKubernetesCluster(clusterId: string): Promise { + console.log(`Deleting kubernetes cluster `); + try { + await client.v2.kubernetes.clusters.byCluster_id(clusterId).delete(); + console.log(`Deleted kubernetes cluster `); + } catch (err) { + console.error(`Failed to delete kubernetes cluster :`, err); + } + } +}); \ No newline at end of file diff --git a/tests/integration/monitoring.test.js b/tests/integration/monitoring.test.js new file mode 100644 index 000000000..1a911f550 --- /dev/null +++ b/tests/integration/monitoring.test.js @@ -0,0 +1,528 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const REGION = "nyc3"; +const PREFIX = "test-dots"; +const DROPLET_SIZE = "s-1vcpu-1gb"; +const DROPLET_IMAGE = "ubuntu-22-04-x64"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Monitoring Integration Tests", () => { + let publicKey; + beforeAll(async () => { + const keyName = process.env.SSH_KEY_NAME; + if (!keyName) { + throw new Error("SSH_KEY_NAME not set"); + } + const sshKey = await findSshKey(keyName); + const fingerprint = sshKey.fingerprint ?? (() => { throw new Error("SSH key fingerprint is undefined or null"); })(); + publicKey = fingerprint; + }); + it("should create, list, get, update, and delete an alert policy", async () => { + const testDropletReq = { + name: `${PREFIX}-${uuidv4()}`, + region: REGION, + size: DROPLET_SIZE, + image: DROPLET_IMAGE, + tags: ["cg_test_tag"], + ssh_keys: [publicKey.toString()], + }; + const droplet = await createDroplet(testDropletReq); + try { + await waitForDropletActive(droplet.id); + const dropletGetResp = await getDroplet(droplet.id); + expect(dropletGetResp.status).toBe("active"); + const dropletId = dropletGetResp.id; + const createAlertReq = { + alerts: { + email: ["mchittupolu@digitalocean.com"], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: [dropletId.toString()], + tags: ["cg_test_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + const createAlertResp = await createAlertPolicy(createAlertReq); + if (!createAlertResp?.policy?.entities) { + throw new Error(`Policy entities not found`); + } + expect(createAlertResp.policy.entities[0]).toBe(dropletId.toString()); + const alertUuid = createAlertResp?.policy?.uuid; + if (!alertUuid) { + throw new Error(`Alert policy with ID ${alertUuid} not found`); + } + try { + const alertPoliciesList = await listAlertPolicies(); + expect(alertPoliciesList.length).toBeGreaterThan(0); + const getAlertPolicy = await getAlertPolicyById(alertUuid); + if (!getAlertPolicy?.policy?.entities) { + throw new Error(`Alert entities with ID ${alertUuid} not found`); + } + expect(getAlertPolicy?.policy?.entities[0]).toBe(dropletId.toString()); + const updateAlertReq = { + alerts: { + email: ["mchittupolu@digitalocean.com"], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + tags: ["cg_test_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + const updateAlertPolicy = await updateAlertPolicyById(alertUuid, updateAlertReq); + if (!updateAlertPolicy?.policy?.alerts?.email) { + throw new Error(`Alert emaail with ID ${alertUuid} not found`); + } + expect(updateAlertPolicy.policy.alerts.email).toContain("mchittupolu@digitalocean.com"); + await deleteAlertPolicy(alertUuid); + } + catch (alertError) { + try { + await deleteAlertPolicy(alertUuid); + } + catch (cleanupError) { + console.warn("Failed to clean up alert policy:", cleanupError); + } + throw alertError; + } + } + finally { + await deleteDroplet(droplet.id); + } + }, 120000); + it("should get various metrics", async () => { + const testDropletReq = { + name: `${PREFIX}-${uuidv4()}`, + region: REGION, + size: DROPLET_SIZE, + image: DROPLET_IMAGE, + tags: ["cg_test_tag"], + ssh_keys: [publicKey.toString()], + }; + const droplet = await createDroplet(testDropletReq); + try { + await waitForDropletActive(droplet.id); + const dropletGetResp = await getDroplet(droplet.id); + expect(dropletGetResp.status).toBe("active"); + const dropletId = dropletGetResp.id; + const endTime = Math.floor(Date.now() / 1000); + const startTime = endTime - (6 * 60 * 60); + const bandwidthMetric = await getDropletBandwidthMetrics(dropletId.toString(), "public", "outbound", startTime.toString(), endTime.toString()); + expect(bandwidthMetric.status).toBe("success"); + const cpuMetric = await getDropletCpuMetrics(dropletId.toString(), startTime.toString(), endTime.toString()); + expect(cpuMetric.status).toBe("success"); + const filesystemFreeMetric = await getDropletFilesystemFreeMetrics(dropletId.toString(), startTime.toString(), endTime.toString()); + expect(filesystemFreeMetric.status).toBe("success"); + const load1Metric = await getDropletLoad1Metrics(dropletId.toString(), startTime.toString(), endTime.toString()); + expect(load1Metric.status).toBe("success"); + const load5Metric = await getDropletLoad5Metrics(dropletId.toString(), startTime.toString(), endTime.toString()); + expect(load5Metric.status).toBe("success"); + const load15Metric = await getDropletLoad15Metrics(dropletId.toString(), startTime.toString(), endTime.toString()); + expect(load15Metric.status).toBe("success"); + const memoryCachedMetric = await getDropletMemoryCachedMetrics(dropletId.toString(), startTime.toString(), endTime.toString()); + expect(memoryCachedMetric.status).toBe("success"); + const memoryFreeMetric = await getDropletMemoryFreeMetrics(dropletId.toString(), startTime.toString(), endTime.toString()); + expect(memoryFreeMetric.status).toBe("success"); + const memoryTotalMetric = await getDropletMemoryTotalMetrics(dropletId.toString(), startTime.toString(), endTime.toString()); + expect(memoryTotalMetric.status).toBe("success"); + const memoryAvailableMetric = await getDropletMemoryAvailableMetrics(dropletId.toString(), startTime.toString(), endTime.toString()); + expect(memoryAvailableMetric.status).toBe("success"); + } + finally { + await deleteDroplet(droplet.id); + } + }, 120000); + async function findSshKey(name) { + console.log(`Looking for SSH key named ${name}...`); + let paginated = true; + while (paginated) { + try { + const resp = await client.v2.account.keys.get(); + if (resp && resp.sshKeys) { + for (const k of resp.sshKeys) { + if (k.name === name) { + console.log(`Found SSH key: ${k.fingerprint}`); + return k; + } + } + const pages = resp.links?.pages; + if (pages && 'next' in pages) { + const nextUrl = pages.next; + if (nextUrl) { + const parsedUrl = new URL(nextUrl); + const nextPage = parseInt(parsedUrl.searchParams.get("page")); + console.log(`Next page: ${nextPage}`); + } + else { + paginated = false; + } + } + else { + console.log("No next page available"); + paginated = false; + } + } + else { + throw new Error("Failed to retrieve SSH keys"); + } + } + catch (err) { + console.error(`Error: ${err}`); + throw err; + } + } + throw new Error("No SSH key found"); + } + async function createDroplet(req) { + console.log(`Creating Droplet using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.droplets.post(req); + if (resp && 'droplet' in resp) { + const dropletId = resp.droplet?.id; + if (dropletId) { + const droplet = await waitForDropletNetworks(dropletId); + return droplet; + } + else { + throw new Error("Droplet ID is undefined"); + } + } + else { + throw new Error("Failed to create droplet"); + } + } + catch (err) { + handleError(err); + } + } + async function waitForDropletNetworks(dropletId) { + let attempts = 0; + const maxAttempts = 10; + const delay = 5000; + while (attempts < maxAttempts) { + const getResp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (getResp && 'droplet' in getResp) { + const droplet = getResp.droplet; + if (droplet?.networks && droplet.networks.v4 && droplet.networks.v4.length > 0) { + return { + id: droplet.id, + name: droplet.name, + region: droplet.region, + size: droplet.size, + image: droplet.image, + status: droplet.status, + networks: { + v4: (droplet.networks?.v4 + ?.filter((net) => typeof net.ipAddress === "string" && !!net.type) + .map((net) => ({ + ip_address: net.ipAddress, + type: net.type, + }))) ?? [], + }, + }; + } + } + attempts++; + await new Promise(resolve => setTimeout(resolve, delay)); + } + throw new Error("Failed to retrieve droplet details or networks information"); + } + async function waitForDropletActive(dropletId) { + console.log(`Waiting for droplet ${dropletId} to become active...`); + let attempts = 0; + const maxAttempts = 20; + const delay = 5000; + while (attempts < maxAttempts) { + const resp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (resp && resp.droplet && resp.droplet.status === "active") { + console.log(`Droplet ${dropletId} is now active`); + return; + } + attempts++; + await new Promise(resolve => setTimeout(resolve, delay)); + } + throw new Error(`Droplet ${dropletId} did not become active within timeout`); + } + async function getDroplet(dropletId) { + try { + const resp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (resp && resp.droplet) { + const droplet = resp.droplet; + return { + id: droplet.id, + name: droplet.name, + region: droplet.region, + size: droplet.size, + image: droplet.image, + status: droplet.status, + networks: { + v4: (droplet.networks?.v4 + ?.filter((net) => typeof net.ipAddress === "string" && !!net.type) + .map((net) => ({ + ip_address: net.ipAddress, + type: net.type, + }))) ?? [], + }, + }; + } + else { + throw new Error("Failed to get droplet"); + } + } + catch (err) { + handleError(err); + } + } + async function deleteDroplet(dropletId) { + console.log(`Deleting droplet: ${dropletId}`); + try { + await client.v2.droplets.byDroplet_id(dropletId).delete(); + } + catch (err) { + handleError(err); + } + } + async function createAlertPolicy(req) { + console.log(`Creating alert policy: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.monitoring.alerts.post(req); + if (resp && resp.policy) { + console.log(resp); + return resp; + } + else { + throw new Error("Failed to create alert policy"); + } + } + catch (err) { + handleError(err); + } + } + async function listAlertPolicies() { + try { + const resp = await client.v2.monitoring.alerts.get(); + if (resp && resp.policies) { + return resp.policies; + } + else { + return []; + } + } + catch (err) { + handleError(err); + } + } + async function getAlertPolicyById(alertUuid) { + try { + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).get(); + if (resp && resp.policy) { + return resp; + } + else { + throw new Error("Failed to get alert policy"); + } + } + catch (err) { + handleError(err); + } + } + async function updateAlertPolicyById(alertUuid, req) { + console.log(`Updating alert policy: ${alertUuid}`); + try { + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).put(req); + if (resp && resp.policy) { + return resp; + } + else { + throw new Error("Failed to update alert policy"); + } + } + catch (err) { + handleError(err); + } + } + async function deleteAlertPolicy(alertUuid) { + console.log(`Deleting alert policy: ${alertUuid}`); + try { + await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).delete(); + } + catch (err) { + handleError(err); + } + } + async function getDropletBandwidthMetrics(hostId, interfaceType, direction, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.bandwidth.get({ + queryParameters: { + hostId: hostId, + interface: interfaceType, + direction: direction, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + async function getDropletCpuMetrics(hostId, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.cpu.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + async function getDropletFilesystemFreeMetrics(hostId, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.filesystem_free.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + async function getDropletLoad1Metrics(hostId, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.load_1.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + async function getDropletLoad5Metrics(hostId, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.load_5.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + async function getDropletLoad15Metrics(hostId, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.load_15.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + async function getDropletMemoryCachedMetrics(hostId, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.memory_cached.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + async function getDropletMemoryFreeMetrics(hostId, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.memory_free.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + async function getDropletMemoryTotalMetrics(hostId, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.memory_total.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + async function getDropletMemoryAvailableMetrics(hostId, start, end) { + try { + const resp = await client.v2.monitoring.metrics.droplet.memory_available.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } + catch (err) { + handleError(err); + } + } + function handleError(err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } +}); diff --git a/tests/integration/monitoring.test.ts b/tests/integration/monitoring.test.ts new file mode 100644 index 000000000..cb6f5b17c --- /dev/null +++ b/tests/integration/monitoring.test.ts @@ -0,0 +1,608 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +import { Droplet_multi_create } from "../../src/dots/models/index.js"; +import { Alert_policy_request } from "../../src/dots/models/index.js"; +import { AlertsPostResponse } from "../../src/dots/v2/monitoring/alerts/index.js"; +import { GetDirectionQueryParameterType, GetInterfaceQueryParameterType } from "../../src/dots/v2/monitoring/metrics/droplet/bandwidth/index.js"; +import { Metrics } from "../../src/dots/models/index.js"; +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const REGION = "nyc3"; +const PREFIX = "test-dots"; +const DROPLET_SIZE = "s-1vcpu-1gb"; +const DROPLET_IMAGE = "ubuntu-22-04-x64"; + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +interface Droplet { + id: number; + name: string; + region: string; + size: string; + image: string; + status: string; + networks: { + v4: { ip_address: string; type: string }[]; + }; +} + +interface DropletRequest { + name: string; + region: string; + size: string; + image: string; + ssh_keys?: string[]; + tags?: string[]; +} + + +describe("Monitoring Integration Tests", () => { + let publicKey : string; + beforeAll(async () => { + const keyName = process.env.SSH_KEY_NAME; + if (!keyName) { + throw new Error("SSH_KEY_NAME not set"); + } + const sshKey = await findSshKey(keyName); + const fingerprint = sshKey.fingerprint ?? (() => { throw new Error("SSH key fingerprint is undefined or null"); })(); + publicKey = fingerprint; + }); + + it("should create, list, get, update, and delete an alert policy", async () => { + const testDropletReq: DropletRequest = { + name: `${PREFIX}-${uuidv4()}`, + region: REGION, + size: DROPLET_SIZE, + image: DROPLET_IMAGE, + tags: ["cg_test_tag"], + ssh_keys: [publicKey.toString()], + }; + const droplet = await createDroplet(testDropletReq); + + try { + await waitForDropletActive(droplet.id); + const dropletGetResp = await getDroplet(droplet.id); + expect(dropletGetResp.status).toBe("active"); + const dropletId = dropletGetResp.id; + + const createAlertReq: Alert_policy_request = { + alerts: { + email: ["mchittupolu@digitalocean.com"], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: [dropletId.toString()], + tags: ["cg_test_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + const createAlertResp = await createAlertPolicy(createAlertReq); + if (!createAlertResp?.policy?.entities) { + throw new Error(`Policy entities not found`); + } + expect(createAlertResp.policy.entities[0]).toBe(dropletId.toString()); + const alertUuid = createAlertResp?.policy?.uuid; + if (!alertUuid) { + throw new Error(`Alert policy with ID ${alertUuid} not found`); + } + try { + const alertPoliciesList = await listAlertPolicies(); + expect(alertPoliciesList.length).toBeGreaterThan(0); + const getAlertPolicy = await getAlertPolicyById(alertUuid); + if (!getAlertPolicy?.policy?.entities) { + throw new Error(`Alert entities with ID ${alertUuid} not found`); + } + expect(getAlertPolicy?.policy?.entities[0]).toBe(dropletId.toString()); + + const updateAlertReq: Alert_policy_request = { + alerts: { + email: ["mchittupolu@digitalocean.com"], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + tags: ["cg_test_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + const updateAlertPolicy = await updateAlertPolicyById(alertUuid, updateAlertReq); + if (!updateAlertPolicy?.policy?.alerts?.email) { + throw new Error(`Alert emaail with ID ${alertUuid} not found`); + } + expect(updateAlertPolicy.policy.alerts.email).toContain("mchittupolu@digitalocean.com"); + await deleteAlertPolicy(alertUuid); + + } catch (alertError) { + try { + await deleteAlertPolicy(alertUuid); + } catch (cleanupError) { + console.warn("Failed to clean up alert policy:", cleanupError); + } + throw alertError; + } + + } finally { + await deleteDroplet(droplet.id); + } + }, 120000); + + it("should get various metrics", async () => { + const testDropletReq: DropletRequest = { + name: `${PREFIX}-${uuidv4()}`, + region: REGION, + size: DROPLET_SIZE, + image: DROPLET_IMAGE, + tags: ["cg_test_tag"], + ssh_keys: [publicKey.toString()], + }; + + const droplet = await createDroplet(testDropletReq); + + try { + await waitForDropletActive(droplet.id); + const dropletGetResp = await getDroplet(droplet.id); + expect(dropletGetResp.status).toBe("active"); + const dropletId = dropletGetResp.id; + const endTime = Math.floor(Date.now() / 1000); + const startTime = endTime - (6 * 60 * 60); + const bandwidthMetric = await getDropletBandwidthMetrics( + dropletId.toString(), + "public", + "outbound", + startTime.toString(), + endTime.toString() + ); + expect(bandwidthMetric.status).toBe("success"); + const cpuMetric = await getDropletCpuMetrics( + dropletId.toString(), + startTime.toString(), + endTime.toString() + ); + expect(cpuMetric.status).toBe("success"); + const filesystemFreeMetric = await getDropletFilesystemFreeMetrics( + dropletId.toString(), + startTime.toString(), + endTime.toString() + ); + expect(filesystemFreeMetric.status).toBe("success"); + const load1Metric = await getDropletLoad1Metrics( + dropletId.toString(), + startTime.toString(), + endTime.toString() + ); + expect(load1Metric.status).toBe("success"); + const load5Metric = await getDropletLoad5Metrics( + dropletId.toString(), + startTime.toString(), + endTime.toString() + ); + expect(load5Metric.status).toBe("success"); + const load15Metric = await getDropletLoad15Metrics( + dropletId.toString(), + startTime.toString(), + endTime.toString() + ); + expect(load15Metric.status).toBe("success"); + const memoryCachedMetric = await getDropletMemoryCachedMetrics( + dropletId.toString(), + startTime.toString(), + endTime.toString() + ); + expect(memoryCachedMetric.status).toBe("success"); + const memoryFreeMetric = await getDropletMemoryFreeMetrics( + dropletId.toString(), + startTime.toString(), + endTime.toString() + ); + expect(memoryFreeMetric.status).toBe("success"); + const memoryTotalMetric = await getDropletMemoryTotalMetrics( + dropletId.toString(), + startTime.toString(), + endTime.toString() + ); + expect(memoryTotalMetric.status).toBe("success"); + const memoryAvailableMetric = await getDropletMemoryAvailableMetrics( + dropletId.toString(), + startTime.toString(), + endTime.toString() + ); + expect(memoryAvailableMetric.status).toBe("success"); + + } finally { + await deleteDroplet(droplet.id); + } + }, 120000); + + + async function findSshKey(name: string): Promise<{ name?: string | null; fingerprint?: string | null }> { + console.log(`Looking for SSH key named ${name}...`); + let paginated = true; + while (paginated) { + try { + const resp = await client.v2.account.keys.get(); + if (resp && resp.sshKeys) { + for (const k of resp.sshKeys) { + if (k.name === name) { + console.log(`Found SSH key: ${k.fingerprint}`); + return k; + } + } + const pages = resp.links?.pages; + if (pages && 'next' in pages) { + const nextUrl = pages.next; + if (nextUrl) { + const parsedUrl = new URL(nextUrl); + const nextPage = parseInt(parsedUrl.searchParams.get("page")!); + console.log(`Next page: ${nextPage}`); + } else { + paginated = false; + } + } else { + console.log("No next page available"); + paginated = false; + } + } else { + throw new Error("Failed to retrieve SSH keys"); + } + } catch (err) { + console.error(`Error: ${err}`); + throw err; + } + } + throw new Error("No SSH key found"); + } + + async function createDroplet(req: Droplet_multi_create): Promise { + console.log(`Creating Droplet using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.droplets.post(req); + if (resp && 'droplet' in resp) { + const dropletId = resp.droplet?.id; + if (dropletId) { + const droplet = await waitForDropletNetworks(dropletId); + return droplet; + } else { + throw new Error("Droplet ID is undefined"); + } + } else { + throw new Error("Failed to create droplet"); + } + } catch (err) { + handleError(err); + } + } + + async function waitForDropletNetworks(dropletId: number): Promise { + let attempts = 0; + const maxAttempts = 10; + const delay = 5000; + + while (attempts < maxAttempts) { + const getResp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (getResp && 'droplet' in getResp) { + const droplet = getResp.droplet; + if (droplet?.networks && droplet.networks.v4 && droplet.networks.v4.length > 0) { + return { + id: droplet.id as number, + name: droplet.name as string, + region: droplet.region as string, + size: droplet.size as string, + image: droplet.image as string, + status: droplet.status as string, + networks: { + v4: (droplet.networks?.v4 + ?.filter((net: { ipAddress?: string | null; type?: string | null }) => + typeof net.ipAddress === "string" && !!net.type + ) + .map((net: { ipAddress?: string | null; type?: string | null }) => ({ + ip_address: net.ipAddress as string, + type: net.type as string, + })) + ) ?? [], + }, + }; + } + } + attempts++; + await new Promise(resolve => setTimeout(resolve, delay)); + } + throw new Error("Failed to retrieve droplet details or networks information"); + } + + async function waitForDropletActive(dropletId: number): Promise { + console.log(`Waiting for droplet ${dropletId} to become active...`); + let attempts = 0; + const maxAttempts = 20; + const delay = 5000; + + while (attempts < maxAttempts) { + const resp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (resp && resp.droplet && resp.droplet.status === "active") { + console.log(`Droplet ${dropletId} is now active`); + return; + } + attempts++; + await new Promise(resolve => setTimeout(resolve, delay)); + } + throw new Error(`Droplet ${dropletId} did not become active within timeout`); + } + + async function getDroplet(dropletId: number): Promise { + try { + const resp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (resp && resp.droplet) { + const droplet = resp.droplet; + return { + id: droplet.id as number, + name: droplet.name as string, + region: droplet.region as string, + size: droplet.size as string, + image: droplet.image as string, + status: droplet.status as string, + networks: { + v4: (droplet.networks?.v4 + ?.filter((net: { ipAddress?: string | null; type?: string | null }) => + typeof net.ipAddress === "string" && !!net.type + ) + .map((net: { ipAddress?: string | null; type?: string | null }) => ({ + ip_address: net.ipAddress as string, + type: net.type as string, + })) + ) ?? [], + }, + }; + } else { + throw new Error("Failed to get droplet"); + } + } catch (err) { + handleError(err); + } + } + + async function deleteDroplet(dropletId: number): Promise { + console.log(`Deleting droplet: ${dropletId}`); + try { + await client.v2.droplets.byDroplet_id(dropletId).delete(); + } catch (err) { + handleError(err); + } + } + async function createAlertPolicy(req: Alert_policy_request): Promise { + console.log(`Creating alert policy: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.monitoring.alerts.post(req); + if (resp && resp.policy) { + console.log(resp); + return resp; + } else { + throw new Error("Failed to create alert policy"); + } + } catch (err) { + handleError(err); + } + } + + async function listAlertPolicies(): Promise { + try { + const resp = await client.v2.monitoring.alerts.get(); + if (resp && resp.policies) { + return resp.policies; + } else { + return []; + } + } catch (err) { + handleError(err); + } + } + + async function getAlertPolicyById(alertUuid: string): Promise { + try { + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).get(); + if (resp && resp.policy) { + return resp; + } else { + throw new Error("Failed to get alert policy"); + } + } catch (err) { + handleError(err); + } + } + + async function updateAlertPolicyById(alertUuid: string, req: Alert_policy_request): Promise { + console.log(`Updating alert policy: ${alertUuid}`); + try { + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).put(req); + if (resp && resp.policy) { + return resp; + } else { + throw new Error("Failed to update alert policy"); + } + } catch (err) { + handleError(err); + } + } + + async function deleteAlertPolicy(alertUuid: string): Promise { + console.log(`Deleting alert policy: ${alertUuid}`); + try { + await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).delete(); + } catch (err) { + handleError(err); + } + } + async function getDropletBandwidthMetrics(hostId: string, interfaceType: GetInterfaceQueryParameterType, direction: GetDirectionQueryParameterType, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.bandwidth.get({ + queryParameters: { + hostId: hostId, + interface: interfaceType, + direction: direction, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + + async function getDropletCpuMetrics(hostId: string, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.cpu.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + + async function getDropletFilesystemFreeMetrics(hostId: string, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.filesystem_free.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + + async function getDropletLoad1Metrics(hostId: string, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.load_1.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + + async function getDropletLoad5Metrics(hostId: string, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.load_5.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + + async function getDropletLoad15Metrics(hostId: string, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.load_15.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + + async function getDropletMemoryCachedMetrics(hostId: string, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.memory_cached.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + + async function getDropletMemoryFreeMetrics(hostId: string, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.memory_free.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + + async function getDropletMemoryTotalMetrics(hostId: string, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.memory_total.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + + async function getDropletMemoryAvailableMetrics(hostId: string, start: string, end: string): Promise { + try { + const resp = await client.v2.monitoring.metrics.droplet.memory_available.get({ + queryParameters: { + hostId: hostId, + start: start, + end: end + } + }); + return { status: resp?.status || "success", data: resp }; + } catch (err) { + handleError(err); + } + } + function handleError(err: unknown): never { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } +}); \ No newline at end of file diff --git a/tests/integration/oneClicks.test.js b/tests/integration/oneClicks.test.js new file mode 100644 index 000000000..e8c3e1764 --- /dev/null +++ b/tests/integration/oneClicks.test.js @@ -0,0 +1,87 @@ +import { v4 as uuidv4 } from 'uuid'; +import { createDigitalOceanClient } from '../../src/dots/digitalOceanClient.js'; +import { DigitalOceanApiKeyAuthenticationProvider } from '../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js'; +import { FetchRequestAdapter } from '@microsoft/kiota-http-fetchlibrary'; +import dotenv from "dotenv"; +dotenv.config(); +const defaults = { + PREFIX: 'dots-test', + REGION: 'nyc3', + K8S_VERSION: '1.33.1-do.2', + K8S_NODE_SIZE: 's-2vcpu-2gb' +}; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(process.env.DIGITALOCEAN_TOKEN); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +async function createTestCluster(client, name) { + return await client.v2.kubernetes.clusters.post({ + name, + region: defaults.REGION, + version: defaults.K8S_VERSION, + nodePools: [ + { + size: defaults.K8S_NODE_SIZE, + count: 2, + name: 'workers' + } + ] + }); +} +async function waitForClusterRunning(client, clusterId, maxWaitTime = 900000, pollInterval = 30000) { + const startTime = Date.now(); + while (Date.now() - startTime < maxWaitTime) { + try { + console.log(`at 49`); + const cluster = await client.v2.kubernetes.clusters.byCluster_id(clusterId).get(); + console.log(`at 50`); + if (cluster?.kubernetesCluster?.status?.state === 'running') { + return cluster; + } + console.log(`Cluster ${clusterId} status: ${cluster?.kubernetesCluster?.status?.state}, waiting...`); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + catch (error) { + console.warn(`Error checking cluster status: ${error}`); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + } + throw new Error(`Cluster ${clusterId} did not reach running state within ${maxWaitTime}ms`); +} +async function deleteCluster(client, clusterId) { + try { + await client.v2.kubernetes.clusters.byCluster_id(clusterId).delete(); + console.log(`Cleaned up cluster ${clusterId}`); + } + catch (error) { + console.warn(`Failed to clean up cluster ${clusterId}:`, error); + } +} +describe('One-clicks Integration Tests', () => { + it('should install kubernetes app via one-click', async () => { + const clusterName = `${defaults.PREFIX}-cluster-${uuidv4()}`; + let clusterId; + try { + // Create cluster + const cluster = await createTestCluster(client, clusterName); + if (!cluster?.kubernetesCluster?.id) { + throw new Error(`Id not found`); + } + clusterId = cluster?.kubernetesCluster?.id; + if (!clusterId) { + throw new Error('Failed to create cluster'); + } + await waitForClusterRunning(client, clusterId); + const installResp = await client.v2.oneClicks.kubernetes.post({ + addonSlugs: ['kube-state-metrics', 'loki'], + clusterUuid: clusterId + }); + expect(installResp).not.toBeNull(); + expect(installResp?.message).toBe('Successfully kicked off addon job.'); + } + finally { + if (clusterId) { + await deleteCluster(client, clusterId); + } + } + }, 1000000); +}); diff --git a/tests/integration/oneClicks.test.ts b/tests/integration/oneClicks.test.ts new file mode 100644 index 000000000..a782639ec --- /dev/null +++ b/tests/integration/oneClicks.test.ts @@ -0,0 +1,103 @@ +import { v4 as uuidv4 } from 'uuid'; +import { createDigitalOceanClient, type DigitalOceanClient } from '../../src/dots/digitalOceanClient.js'; +import { DigitalOceanApiKeyAuthenticationProvider } from '../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js'; +import { FetchRequestAdapter } from '@microsoft/kiota-http-fetchlibrary'; +import { Cluster } from '../../src/dots/models/index.js'; +import dotenv from "dotenv"; + +dotenv.config(); +const defaults = { + PREFIX: 'dots-test', + REGION: 'nyc3', + K8S_VERSION: '1.33.1-do.2', + K8S_NODE_SIZE: 's-2vcpu-2gb' +}; + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(process.env.DIGITALOCEAN_TOKEN!); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +async function createTestCluster(client: DigitalOceanClient, name: string) { + return await client.v2.kubernetes.clusters.post({ + name, + region: defaults.REGION, + version: defaults.K8S_VERSION, + nodePools: [ + { + size: defaults.K8S_NODE_SIZE, + count: 2, + name: 'workers' + } + ] + }); +} + +async function waitForClusterRunning( + client: DigitalOceanClient, + clusterId: string, + maxWaitTime = 900000, + pollInterval = 30000 +): Promise { + const startTime = Date.now(); + + while (Date.now() - startTime < maxWaitTime) { + try { + console.log(`at 49`); + const cluster = await client.v2.kubernetes.clusters.byCluster_id(clusterId).get(); + console.log(`at 50`); + + if (cluster?.kubernetesCluster?.status?.state === 'running') { + return cluster; + } + + console.log(`Cluster ${clusterId} status: ${cluster?.kubernetesCluster?.status?.state}, waiting...`); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } catch (error) { + console.warn(`Error checking cluster status: ${error}`); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + } + + throw new Error(`Cluster ${clusterId} did not reach running state within ${maxWaitTime}ms`); +} + +async function deleteCluster(client: DigitalOceanClient, clusterId: string) { + try { + await client.v2.kubernetes.clusters.byCluster_id(clusterId).delete(); + console.log(`Cleaned up cluster ${clusterId}`); + } catch (error) { + console.warn(`Failed to clean up cluster ${clusterId}:`, error); + } +} + +describe('One-clicks Integration Tests', () => { + it('should install kubernetes app via one-click', async () => { + const clusterName = `${defaults.PREFIX}-cluster-${uuidv4()}`; + let clusterId: string | undefined; + + try { + // Create cluster + const cluster = await createTestCluster(client, clusterName); + if(!cluster?.kubernetesCluster?.id){ + throw new Error(`Id not found`) + } + clusterId = cluster?.kubernetesCluster?.id; + + if (!clusterId) { + throw new Error('Failed to create cluster'); + } + await waitForClusterRunning(client, clusterId); + const installResp = await client.v2.oneClicks.kubernetes.post({ + addonSlugs: ['kube-state-metrics', 'loki'], + clusterUuid: clusterId + }); + + expect(installResp).not.toBeNull(); + expect(installResp?.message).toBe('Successfully kicked off addon job.'); + + } finally { + if (clusterId) { + await deleteCluster(client, clusterId); + } + } + }, 1000000); +}); \ No newline at end of file diff --git a/tests/integration/projects.test.js b/tests/integration/projects.test.js new file mode 100644 index 000000000..f26d021a3 --- /dev/null +++ b/tests/integration/projects.test.js @@ -0,0 +1,135 @@ +/** + * Integration Tests for Projects + */ +import { v4 as uuidv4 } from 'uuid'; +import { createDigitalOceanClient } from '../../src/dots/digitalOceanClient.js'; +import { DigitalOceanApiKeyAuthenticationProvider } from '../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js'; +import { FetchRequestAdapter } from '@microsoft/kiota-http-fetchlibrary'; +import { defaults } from './defaults.js'; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe('Projects Integration Tests', () => { + it('should create, update, patch, list, and delete a project', async () => { + const expectedName = `${defaults.PREFIX}-${uuidv4()}`; + const createReq = { + name: expectedName, + description: "Test project for typescript client", + purpose: "testing", + environment: "Development", + }; + const createResp = await client.v2.projects.post(createReq); + expect(createResp?.project?.name).toBe(expectedName); + const projectId = createResp?.project?.id; + expect(projectId).toBeDefined(); + try { + const getResp = await client.v2.projects.byProject_id(projectId).get(); + expect(getResp?.project?.name).toBe(expectedName); + const updatedName = `${defaults.PREFIX}-${uuidv4()}`; + const updateReq = { + name: updatedName, + description: "Test project for typescript client", + purpose: "testing", + environment: "Development", + isDefault: false, + }; + const updateResp = await client.v2.projects.byProject_id(projectId).put(updateReq); + expect(updateResp?.project?.name).toBe(updatedName); + const patchName = `${defaults.PREFIX}-${uuidv4()}`; + const patchReq = { + name: patchName, + }; + const patchResp = await client.v2.projects.byProject_id(projectId).patch(patchReq); + expect(patchResp?.project?.name).toBe(patchName); + const listResp = await client.v2.projects.get(); + expect(listResp?.projects?.length).toBeGreaterThan(0); + } + finally { + const deleteResp = await client.v2.projects.byProject_id(projectId).delete({ + headers: { + "Content-Type": "application/json" + } + }); + expect(deleteResp).toBeUndefined(); + } + }, 60000); + it('should get, update, and patch the default project', async () => { + const getResp = await client.v2.projects.defaultEscaped.get(); + expect(getResp?.project?.isDefault).toBe(true); + const originalName = getResp?.project?.name; + try { + const expectedName = `${defaults.PREFIX}-${uuidv4()}`; + const updateReq = { + name: expectedName, + description: "Test project for typescript client", + purpose: "testing", + environment: "Development", + isDefault: true, + }; + const updateResp = await client.v2.projects.defaultEscaped.put(updateReq); + expect(updateResp?.project?.name).toBe(expectedName); + expect(updateResp?.project?.isDefault).toBe(true); + const patchName = `${defaults.PREFIX}-${uuidv4()}`; + const patchReq = { + name: patchName, + }; + const patchResp = await client.v2.projects.defaultEscaped.patch(patchReq); + expect(patchResp?.project?.name).toBe(patchName); + expect(patchResp?.project?.isDefault).toBe(true); + } + finally { + if (originalName) { + try { + await client.v2.projects.defaultEscaped.patch({ + name: originalName + }); + } + catch (error) { + console.warn('Failed to restore original default project name:', error); + } + } + } + }, 60000); +}); +describe('Projects Resource Management', () => { + let testProjectId; + beforeEach(async () => { + const projectName = `${defaults.PREFIX}-resources-${uuidv4()}`; + const createReq = { + name: projectName, + description: "Test project for resource management", + purpose: "testing", + environment: "Development", + }; + const createResp = await client.v2.projects.post(createReq); + if (!createResp?.project?.id) { + throw new Error("Failed to create project or project ID is undefined"); + } + testProjectId = createResp.project.id; + }); + afterEach(async () => { + if (testProjectId) { + try { + await client.v2.projects.byProject_id(testProjectId).delete({ + headers: { + "Content-Type": "application/json" + } + }); + } + catch (error) { + console.warn(`Failed to clean up project ${testProjectId}:`, error); + } + } + }); + it('should list project resources', async () => { + const resourcesResp = await client.v2.projects.byProject_id(testProjectId).resources.get(); + expect(resourcesResp?.resources).toBeDefined(); + expect(Array.isArray(resourcesResp?.resources)).toBe(true); + }); +}); diff --git a/tests/integration/projects.test.ts b/tests/integration/projects.test.ts new file mode 100644 index 000000000..ff6af1f32 --- /dev/null +++ b/tests/integration/projects.test.ts @@ -0,0 +1,154 @@ +/** + * Integration Tests for Projects + */ + +import { v4 as uuidv4 } from 'uuid'; +import { createDigitalOceanClient } from '../../src/dots/digitalOceanClient.js'; +import { DigitalOceanApiKeyAuthenticationProvider } from '../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js'; +import { FetchRequestAdapter } from '@microsoft/kiota-http-fetchlibrary'; +import { defaults } from './defaults.js'; +import { Project_base, Project } from '../../src/dots/models/index.js'; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe('Projects Integration Tests', () => { + it('should create, update, patch, list, and delete a project', async () => { + const expectedName = `${defaults.PREFIX}-${uuidv4()}`; + const createReq : Project_base = { + name: expectedName, + description: "Test project for typescript client", + purpose: "testing", + environment: "Development", + }; + + const createResp = await client.v2.projects.post(createReq); + expect(createResp?.project?.name).toBe(expectedName); + + const projectId = createResp?.project?.id; + expect(projectId).toBeDefined(); + + try { + const getResp = await client.v2.projects.byProject_id(projectId!).get(); + expect(getResp?.project?.name).toBe(expectedName); + const updatedName = `${defaults.PREFIX}-${uuidv4()}`; + const updateReq : Project = { + name: updatedName, + description: "Test project for typescript client", + purpose: "testing", + environment: "Development", + isDefault: false, + }; + + const updateResp = await client.v2.projects.byProject_id(projectId!).put(updateReq); + expect(updateResp?.project?.name).toBe(updatedName); + const patchName = `${defaults.PREFIX}-${uuidv4()}`; + const patchReq = { + name: patchName, + }; + + const patchResp = await client.v2.projects.byProject_id(projectId!).patch(patchReq); + expect(patchResp?.project?.name).toBe(patchName); + const listResp = await client.v2.projects.get(); + expect(listResp?.projects?.length).toBeGreaterThan(0); + + } finally { + const deleteResp = await client.v2.projects.byProject_id(projectId!).delete({ + headers: { + "Content-Type": "application/json" + } + }); + expect(deleteResp).toBeUndefined(); + } + }, 60000); + + it('should get, update, and patch the default project', async () => { + const getResp = await client.v2.projects.defaultEscaped.get(); + expect(getResp?.project?.isDefault).toBe(true); + const originalName = getResp?.project?.name; + + try { + const expectedName = `${defaults.PREFIX}-${uuidv4()}`; + const updateReq : Project= { + name: expectedName, + description: "Test project for typescript client", + purpose: "testing", + environment: "Development", + isDefault: true, + }; + + const updateResp = await client.v2.projects.defaultEscaped.put(updateReq); + expect(updateResp?.project?.name).toBe(expectedName); + expect(updateResp?.project?.isDefault).toBe(true); + const patchName = `${defaults.PREFIX}-${uuidv4()}`; + const patchReq = { + name: patchName, + }; + + const patchResp = await client.v2.projects.defaultEscaped.patch(patchReq); + expect(patchResp?.project?.name).toBe(patchName); + expect(patchResp?.project?.isDefault).toBe(true); + + } finally { + if (originalName) { + try { + await client.v2.projects.defaultEscaped.patch({ + name: originalName + }); + } catch (error) { + console.warn('Failed to restore original default project name:', error); + } + } + } + }, 60000); +}); + + describe('Projects Resource Management', () => { + let testProjectId: string; + + beforeEach(async () => { + const projectName = `${defaults.PREFIX}-resources-${uuidv4()}`; + const createReq : Project_base = { + name: projectName, + description: "Test project for resource management", + purpose: "testing", + environment: "Development", + }; + + const createResp = await client.v2.projects.post(createReq); + if (!createResp?.project?.id) { + throw new Error("Failed to create project or project ID is undefined"); + } + testProjectId = createResp.project.id; + }); + + afterEach(async () => { + if (testProjectId) { + try { + await client.v2.projects.byProject_id(testProjectId).delete({ + headers: { + "Content-Type": "application/json" + } + }); + } catch (error) { + console.warn(`Failed to clean up project ${testProjectId}:`, error); + } + } + }); + + it('should list project resources', async () => { + const resourcesResp = await client.v2.projects.byProject_id(testProjectId).resources.get(); + expect(resourcesResp?.resources).toBeDefined(); + expect(Array.isArray(resourcesResp?.resources)).toBe(true); + }); + + }); \ No newline at end of file diff --git a/tests/integration/regions.test.js b/tests/integration/regions.test.js new file mode 100644 index 000000000..651d089c4 --- /dev/null +++ b/tests/integration/regions.test.js @@ -0,0 +1,18 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Region Integration Tests", () => { + it("should return list of regions", async () => { + const listResp = await client.v2.regions.get(); + expect(listResp?.regions?.length).toBeGreaterThan(13); + }); +}); diff --git a/tests/integration/regions.test.ts b/tests/integration/regions.test.ts new file mode 100644 index 000000000..146e15b80 --- /dev/null +++ b/tests/integration/regions.test.ts @@ -0,0 +1,23 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Region Integration Tests", () => { + it("should return list of regions", async () => { + const listResp = await client.v2.regions.get(); + expect(listResp?.regions?.length).toBeGreaterThan(13); + }) +}); \ No newline at end of file diff --git a/tests/integration/reservedIps.test.js b/tests/integration/reservedIps.test.js new file mode 100644 index 000000000..6664f4868 --- /dev/null +++ b/tests/integration/reservedIps.test.js @@ -0,0 +1,238 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const PREFIX = "test"; +const REGION = "nyc3"; +const DROPLET_SIZE = "s-1vcpu-1gb"; +const DROPLET_IMAGE = "ubuntu-22-04-x64"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Reserved IPs Integration Tests", () => { + it("should create, assign, unassign, and delete a reserved IP", async () => { + const dropletName = `${PREFIX}-dots-${uuidv4()}`; + const createDropletReq = { + name: dropletName, + region: REGION, + size: DROPLET_SIZE, + image: DROPLET_IMAGE, + }; + const droplet = await createDroplet(createDropletReq); + try { + if (!droplet.id) { + throw new Error("Droplet ID is missing"); + } + await waitForDropletReady(droplet.id); + const createReservedIpReq = { + region: REGION, + }; + const reservedIp = await createReservedIp(createReservedIpReq); + try { + if (!reservedIp.ip) { + throw new Error("Droplet ID is missing"); + } + const getResp = await client.v2.reserved_ips.byReserved_ip(reservedIp.ip).get(); + if (!getResp || !getResp.reservedIp) { + throw new Error("Failed to get reserved IP"); + } + expect(getResp.reservedIp.ip).toBe(reservedIp.ip); + expect(getResp.reservedIp.region?.slug).toBe(REGION); + expect(getResp.reservedIp.droplet).toEqual({}); + const assignAction = await assignReservedIp(reservedIp.ip, droplet.id); + await waitForAction(assignAction.id); + const assignedResp = await client.v2.reserved_ips.byReserved_ip(reservedIp.ip).get(); + if (!assignedResp || !assignedResp.reservedIp) { + throw new Error("Failed to get assigned reserved IP"); + } + expect(assignedResp.reservedIp.droplet?.id).toBe(droplet.id); + const unassignAction = await unassignReservedIp(reservedIp.ip); + await waitForAction(unassignAction.id); + const unassignedResp = await client.v2.reserved_ips.byReserved_ip(reservedIp.ip).get(); + if (!unassignedResp || !unassignedResp.reservedIp) { + throw new Error("Failed to get unassigned reserved IP"); + } + expect(unassignedResp.reservedIp.droplet).toEqual({}); + expect(unassignedResp.reservedIp.region?.slug).toBe(REGION); + } + finally { + if (!reservedIp.ip) { + throw new Error("Droplet ID is missing"); + } + await deleteReservedIp(reservedIp.ip); + } + } + finally { + if (!droplet.id) { + throw new Error("Droplet ID is missing"); + } + await deleteDroplet(droplet.id); + } + }, 60000); + async function createDroplet(req) { + console.log(`Creating droplet using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.droplets.post(req); + if (resp && 'droplet' in resp && resp.droplet) { + const droplet = resp.droplet; + console.log(`Created droplet ${droplet.id}`); + return droplet; + } + else { + throw new Error("Failed to create droplet or droplet is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + async function createReservedIp(req) { + console.log(`Creating reserved IP using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.reserved_ips.post(req); + if (resp && 'reservedIp' in resp && resp.reservedIp) { + const reservedIp = resp.reservedIp; + console.log(`Created reserved IP ${reservedIp.ip}`); + return resp.reservedIp; + } + else { + throw new Error("Failed to create reserved IP or IP is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + async function assignReservedIp(ip, dropletId) { + console.log(`Assigning reserved IP ${ip} to droplet ${dropletId}`); + try { + const assignRequest = { + type: "assign", + dropletId: dropletId + }; + const resp = await client.v2.reserved_ips.byReserved_ip(ip).actions.post(assignRequest); + if (resp && resp.action) { + const action = resp.action; + console.log(`Assignment action ${action.id} started`); + return { + id: action.id, + type: action.type, + status: action.status, + }; + } + else { + throw new Error("Failed to assign reserved IP or action is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + async function unassignReservedIp(ip) { + console.log(`Unassigning reserved IP ${ip}`); + try { + const unassignRequest = { + type: "unassign", + }; + const resp = await client.v2.reserved_ips.byReserved_ip(ip).actions.post(unassignRequest); + if (resp && resp.action) { + const action = resp.action; + console.log(`Unassignment action ${action.id} started`); + return { + id: action.id, + type: action.type, + status: action.status, + }; + } + else { + throw new Error("Failed to unassign reserved IP or action is undefined"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } + } + async function waitForDropletReady(dropletId) { + console.log(`Waiting for droplet ${dropletId} to be ready`); + let attempts = 0; + const maxAttempts = 30; + while (attempts < maxAttempts) { + const resp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (resp?.droplet?.status === "active") { + console.log(`Droplet ${dropletId} is ready`); + return; + } + await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds + attempts++; + } + throw new Error(`Droplet ${dropletId} did not become ready within expected time`); + } + async function waitForAction(actionId) { + console.log(`Waiting for action ${actionId} to complete`); + let attempts = 0; + const maxAttempts = 30; + while (attempts < maxAttempts) { + const resp = await client.v2.actions.byAction_id(actionId).get(); + if (resp?.action?.status === "completed") { + console.log(`Action ${actionId} completed`); + return; + } + else if (resp?.action?.status === "errored") { + throw new Error(`Action ${actionId} failed`); + } + await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds + attempts++; + } + throw new Error(`Action ${actionId} did not complete within expected time`); + } + async function deleteDroplet(dropletId) { + console.log(`Deleting droplet ${dropletId}`); + try { + await client.v2.droplets.byDroplet_id(dropletId).delete(); + console.log(`Deleted droplet ${dropletId}`); + } + catch (err) { + console.error(`Failed to delete droplet ${dropletId}:`, err); + } + } + async function deleteReservedIp(ip) { + console.log(`Deleting reserved IP ${ip}`); + try { + await client.v2.reserved_ips.byReserved_ip(ip).delete(); + console.log(`Deleted reserved IP ${ip}`); + } + catch (err) { + console.error(`Failed to delete reserved IP ${ip}:`, err); + } + } +}); diff --git a/tests/integration/reservedIps.test.ts b/tests/integration/reservedIps.test.ts new file mode 100644 index 000000000..e7afd2247 --- /dev/null +++ b/tests/integration/reservedIps.test.ts @@ -0,0 +1,269 @@ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { v4 as uuidv4 } from "uuid"; +import { Droplet, Reserved_ip } from "../../src/dots/models/index.js"; +import { Reserved_ip_action_assign } from "../../src/dots/models/index.js"; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} + +const PREFIX = "test"; +const REGION = "nyc3"; +const DROPLET_SIZE = "s-1vcpu-1gb"; +const DROPLET_IMAGE = "ubuntu-22-04-x64"; + +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +interface DropletReq { + name: string; + region: string; + size: string; + image: string; +} + +interface ActionReq { + id: number; + type: string; + status: string; +} +interface ReserverdIp { + region : string +} +describe("Reserved IPs Integration Tests", () => { + it("should create, assign, unassign, and delete a reserved IP", async () => { + const dropletName = `${PREFIX}-dots-${uuidv4()}`; + const createDropletReq: DropletReq = { + name: dropletName, + region: REGION, + size: DROPLET_SIZE, + image:DROPLET_IMAGE, + }; + + const droplet: Droplet = await createDroplet(createDropletReq); + try { + if (!droplet.id) { + throw new Error("Droplet ID is missing"); + } + await waitForDropletReady(droplet.id); + const createReservedIpReq : ReserverdIp= { + region : REGION, + }; + const reservedIp = await createReservedIp(createReservedIpReq); + try { + if (!reservedIp.ip) { + throw new Error("Droplet ID is missing"); + } + const getResp = await client.v2.reserved_ips.byReserved_ip(reservedIp.ip).get(); + if (!getResp || !getResp.reservedIp) { + throw new Error("Failed to get reserved IP"); + } + expect(getResp.reservedIp.ip).toBe(reservedIp.ip); + expect(getResp.reservedIp.region?.slug).toBe(REGION); + expect(getResp.reservedIp.droplet).toEqual({}); + const assignAction: ActionReq = await assignReservedIp(reservedIp.ip, droplet.id); + await waitForAction(assignAction.id); + const assignedResp = await client.v2.reserved_ips.byReserved_ip(reservedIp.ip).get(); + if (!assignedResp || !assignedResp.reservedIp) { + throw new Error("Failed to get assigned reserved IP"); + } + expect(assignedResp.reservedIp.droplet?.id).toBe(droplet.id); + const unassignAction: ActionReq = await unassignReservedIp(reservedIp.ip); + await waitForAction(unassignAction.id); + const unassignedResp = await client.v2.reserved_ips.byReserved_ip(reservedIp.ip).get(); + if (!unassignedResp || !unassignedResp.reservedIp) { + throw new Error("Failed to get unassigned reserved IP"); + } + expect(unassignedResp.reservedIp.droplet).toEqual({}); + expect(unassignedResp.reservedIp.region?.slug).toBe(REGION); + + } finally { + if (!reservedIp.ip) { + throw new Error("Droplet ID is missing"); + } + await deleteReservedIp(reservedIp.ip); + } + + } finally { + if (!droplet.id) { + throw new Error("Droplet ID is missing"); + } + await deleteDroplet(droplet.id); + } + }, 60000); + + async function createDroplet(req: DropletReq): Promise { + console.log(`Creating droplet using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.droplets.post(req); + if (resp && 'droplet' in resp && resp.droplet) { + const droplet = resp.droplet; + console.log(`Created droplet ${droplet.id}`); + return droplet; + } else { + throw new Error("Failed to create droplet or droplet is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + + async function createReservedIp(req: ReserverdIp): Promise { + console.log(`Creating reserved IP using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.reserved_ips.post(req); + if (resp && 'reservedIp' in resp && resp.reservedIp) { + const reservedIp = resp.reservedIp; + console.log(`Created reserved IP ${reservedIp.ip}`); + return resp.reservedIp; + } else { + throw new Error("Failed to create reserved IP or IP is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + + async function assignReservedIp(ip: string, dropletId : number): Promise { + console.log(`Assigning reserved IP ${ip} to droplet ${dropletId}`); + try { + const assignRequest : Reserved_ip_action_assign= { + type: "assign", + dropletId: dropletId + }; + const resp = await client.v2.reserved_ips.byReserved_ip(ip).actions.post(assignRequest); + if (resp && resp.action) { + const action = resp.action; + console.log(`Assignment action ${action.id} started`); + return { + id: action.id as number, + type: action.type as string, + status: action.status as string, + }; + } else { + throw new Error("Failed to assign reserved IP or action is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + + async function unassignReservedIp(ip: string): Promise { + console.log(`Unassigning reserved IP ${ip}`); + try { + const unassignRequest: Reserved_ip_action_assign = { + type: "unassign", + }; + const resp = await client.v2.reserved_ips.byReserved_ip(ip).actions.post(unassignRequest); + if (resp && resp.action) { + const action = resp.action; + console.log(`Unassignment action ${action.id} started`); + return { + id: action.id as number, + type: action.type as string, + status: action.status as string, + }; + } else { + throw new Error("Failed to unassign reserved IP or action is undefined"); + } + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } + } + + async function waitForDropletReady(dropletId: number): Promise { + console.log(`Waiting for droplet ${dropletId} to be ready`); + let attempts = 0; + const maxAttempts = 30; + + while (attempts < maxAttempts) { + const resp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (resp?.droplet?.status === "active") { + console.log(`Droplet ${dropletId} is ready`); + return; + } + await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds + attempts++; + } + throw new Error(`Droplet ${dropletId} did not become ready within expected time`); + } + + async function waitForAction(actionId: number): Promise { + console.log(`Waiting for action ${actionId} to complete`); + let attempts = 0; + const maxAttempts = 30; + + while (attempts < maxAttempts) { + const resp = await client.v2.actions.byAction_id(actionId).get(); + if (resp?.action?.status === "completed") { + console.log(`Action ${actionId} completed`); + return; + } else if (resp?.action?.status === "errored") { + throw new Error(`Action ${actionId} failed`); + } + await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds + attempts++; + } + throw new Error(`Action ${actionId} did not complete within expected time`); + } + + async function deleteDroplet(dropletId: number): Promise { + console.log(`Deleting droplet ${dropletId}`); + try { + await client.v2.droplets.byDroplet_id(dropletId).delete(); + console.log(`Deleted droplet ${dropletId}`); + } catch (err) { + console.error(`Failed to delete droplet ${dropletId}:`, err); + } + } + + async function deleteReservedIp(ip: string): Promise { + console.log(`Deleting reserved IP ${ip}`); + try { + await client.v2.reserved_ips.byReserved_ip(ip).delete(); + console.log(`Deleted reserved IP ${ip}`); + } catch (err) { + console.error(`Failed to delete reserved IP ${ip}:`, err); + } + } + +}); \ No newline at end of file diff --git a/tests/integration/shared.js b/tests/integration/shared.js new file mode 100644 index 000000000..69cebf103 --- /dev/null +++ b/tests/integration/shared.js @@ -0,0 +1,53 @@ +export const defaults = { + PREFIX: 'dots-test', + REGION: 'nyc3', + K8S_VERSION: '1.33.1-do.2', + K8S_NODE_SIZE: 's-2vcpu-2gb' +}; +export const shared = { + async withTestKubernetesCluster(client, clusterCreateReq, options = {}, callback) { + const createResp = await client.v2.kubernetes.clusters.post(clusterCreateReq); + console.log(`at 17`, createResp); + const clusterId = createResp?.kubernetesCluster?.id; + console.log(`cluster Id `, clusterId); + if (!clusterId) { + throw new Error('Failed to create cluster'); + } + try { + let cluster = createResp; + if (options.wait) { + console.log(`at 28`, cluster); + cluster = await waitForClusterRunning(client, clusterId); + console.log(`at 29`, clusterId); + } + return await callback(cluster); + } + finally { + try { + await client.v2.kubernetes.clusters.byCluster_id(clusterId).delete(); + } + catch (error) { + console.warn(`Failed to clean up cluster ${clusterId}:`, error); + } + } + } +}; +async function waitForClusterRunning(client, clusterId, maxWaitTime = 900000, pollInterval = 30000) { + const startTime = Date.now(); + while (Date.now() - startTime < maxWaitTime) { + try { + console.log(clusterId); + const cluster = await client.v2.kubernetes.clusters.byCluster_id(clusterId).get(); + if (cluster?.kubernetesCluster?.status?.state === 'running') { + return cluster; + } + console.log(`Cluster ${clusterId} status: ${cluster?.kubernetesCluster?.status?.state}, waiting...`); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + catch (error) { + console.warn(`Error checking cluster status: ${error}`); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + } + throw new Error(`Cluster ${clusterId} did not reach running state within ${maxWaitTime}ms`); +} diff --git a/tests/integration/shared.ts b/tests/integration/shared.ts new file mode 100644 index 000000000..3f3e863ad --- /dev/null +++ b/tests/integration/shared.ts @@ -0,0 +1,69 @@ +import { createDigitalOceanClient } from '../../src/dots/digitalOceanClient.js'; +export const defaults = { + PREFIX: 'dots-test', + REGION: 'nyc3', + K8S_VERSION: '1.33.1-do.2', + K8S_NODE_SIZE: 's-2vcpu-2gb' +}; +import { Cluster } from '../../src/dots/models/index.js'; +export const shared = { + async withTestKubernetesCluster( + client: ReturnType, + clusterCreateReq: Cluster, + options: { wait?: boolean } = {}, + callback: (cluster: Cluster) => Promise + ): Promise { + const createResp = await client.v2.kubernetes.clusters.post(clusterCreateReq); + console.log(`at 17`, createResp); + const clusterId = createResp?.kubernetesCluster?.id; + console.log(`cluster Id `, clusterId); + + if (!clusterId) { + throw new Error('Failed to create cluster'); + } + + try { + let cluster = createResp; + if (options.wait) { + console.log(`at 28`, cluster); + cluster = await waitForClusterRunning(client, clusterId); + console.log(`at 29`, clusterId); + } + return await callback(cluster); + } finally { + try { + await client.v2.kubernetes.clusters.byCluster_id(clusterId).delete(); + } catch (error) { + console.warn(`Failed to clean up cluster ${clusterId}:`, error); + } + } + } +}; + +async function waitForClusterRunning( + client: ReturnType, + clusterId: string, + maxWaitTime = 900000, + pollInterval = 30000 +): Promise { + const startTime = Date.now(); + + while (Date.now() - startTime < maxWaitTime) { + try { + console.log(clusterId); + const cluster = await client.v2.kubernetes.clusters.byCluster_id(clusterId).get(); + + if (cluster?.kubernetesCluster?.status?.state === 'running') { + return cluster; + } + + console.log(`Cluster ${clusterId} status: ${cluster?.kubernetesCluster?.status?.state}, waiting...`); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } catch (error) { + console.warn(`Error checking cluster status: ${error}`); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + } + + throw new Error(`Cluster ${clusterId} did not reach running state within ${maxWaitTime}ms`); +} diff --git a/tests/integration/sizes.test.js b/tests/integration/sizes.test.js new file mode 100644 index 000000000..53f77f399 --- /dev/null +++ b/tests/integration/sizes.test.js @@ -0,0 +1,26 @@ +/** + * Integration Test for Sizes + */ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Sizes API Integration Tests", () => { + it("should list sizes", async () => { + const listResp = await client.v2.sizes.get(); + expect(listResp && Array.isArray(listResp.sizes)).toBeTruthy(); + expect(listResp?.sizes?.length ?? 0).toBeGreaterThanOrEqual(20); + }); + it("should list sizes asynchronously", async () => { + const listResp = await client.v2.sizes.get(); + expect(listResp?.sizes?.length ?? 0).toBeGreaterThanOrEqual(20); + }); +}); diff --git a/tests/integration/sizes.test.ts b/tests/integration/sizes.test.ts new file mode 100644 index 000000000..ed40a9f48 --- /dev/null +++ b/tests/integration/sizes.test.ts @@ -0,0 +1,32 @@ +/** + * Integration Test for Sizes + */ + +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + + +describe("Sizes API Integration Tests", () => { + it("should list sizes", async () => { + const listResp = await client.v2.sizes.get(); + expect(listResp && Array.isArray(listResp.sizes)).toBeTruthy(); + expect(listResp?.sizes?.length ?? 0).toBeGreaterThanOrEqual(20); + }); + + it("should list sizes asynchronously", async () => { + const listResp = await client.v2.sizes.get(); + expect(listResp?.sizes?.length ?? 0).toBeGreaterThanOrEqual(20); + }); +}); \ No newline at end of file diff --git a/tests/integration/snapshots.test.js b/tests/integration/snapshots.test.js new file mode 100644 index 000000000..deb3b16c9 --- /dev/null +++ b/tests/integration/snapshots.test.js @@ -0,0 +1,68 @@ +/** + * Integration tests for snapshots. + */ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +import { v4 as uuidv4 } from "uuid"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +const PREFIX = "test"; +const REGION = "nyc3"; +describe("Snapshots API Integration Tests", () => { + it("should list, retrieve, and delete a snapshot", async () => { + const volumeReq = { + sizeGigabytes: 10, + name: `${PREFIX}-${uuidv4()}`, + description: "Snapshots testing", + region: REGION, + filesystemType: "ext4", + }; + const volumeResp = await client.v2.volumes.post(volumeReq); + expect(volumeResp).toBeDefined(); + const volId = volumeResp?.volume?.id; + expect(volId).toBeDefined(); + try { + const expectedName = `${PREFIX}-${uuidv4()}`; + if (!volId) { + throw new Error(`VolId is not defined`); + } + const volAttachResp = await client.v2.volumes.byVolume_id(volId).snapshots.post({ + name: expectedName, + }); + expect(volAttachResp).toBeDefined(); + expect(volAttachResp?.snapshot?.name).toBe(expectedName); + console.log(volAttachResp); + const snapshotId = volAttachResp?.snapshot?.id; + expect(snapshotId).toBeDefined(); + const listResp = await client.v2.snapshots.get(); + expect(listResp).toBeDefined(); + expect(listResp?.snapshots?.length).toBeGreaterThan(0); + if (!snapshotId) { + throw new Error(`VolId is not defined`); + } + const getResp = await client.v2.snapshots.bySnapshot_id(snapshotId).get(); + expect(getResp).toBeDefined(); + if (getResp) { + expect(getResp.snapshot?.name).toBe(expectedName); + } + if (!snapshotId) { + throw new Error(`Snapshot is not defined`); + } + const deleteResp = await client.v2.snapshots.bySnapshot_id(snapshotId).delete(); + expect(deleteResp).toBeUndefined(); + } + finally { + if (volId) { + await client.v2.volumes.byVolume_id(volId).delete(); + } + } + }, 60000); +}); diff --git a/tests/integration/snapshots.test.ts b/tests/integration/snapshots.test.ts new file mode 100644 index 000000000..e89fc6435 --- /dev/null +++ b/tests/integration/snapshots.test.ts @@ -0,0 +1,73 @@ +/** + * Integration tests for snapshots. + */ + +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +import { v4 as uuidv4 } from "uuid"; +import { Volumes_ext4 } from "../../src/dots/models/index.js"; +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +const PREFIX = "test"; +const REGION = "nyc3"; + +describe("Snapshots API Integration Tests", () => { + it("should list, retrieve, and delete a snapshot", async () => { + const volumeReq : Volumes_ext4= { + sizeGigabytes: 10, + name: `${PREFIX}-${uuidv4()}`, + description: "Snapshots testing", + region: REGION, + filesystemType: "ext4", + }; + const volumeResp = await client.v2.volumes.post(volumeReq); + expect(volumeResp).toBeDefined(); + const volId = volumeResp?.volume?.id; + expect(volId).toBeDefined(); + + try { + const expectedName = `${PREFIX}-${uuidv4()}`; + if(!volId){ + throw new Error(`VolId is not defined`); + } + const volAttachResp = await client.v2.volumes.byVolume_id(volId).snapshots.post({ + name: expectedName, + }); + expect(volAttachResp).toBeDefined(); + expect(volAttachResp?.snapshot?.name).toBe(expectedName); + console.log(volAttachResp); + const snapshotId = volAttachResp?.snapshot?.id; + expect(snapshotId).toBeDefined(); + const listResp = await client.v2.snapshots.get(); + expect(listResp).toBeDefined(); + expect(listResp?.snapshots?.length).toBeGreaterThan(0); + if(!snapshotId){ + throw new Error(`VolId is not defined`); + } + const getResp = await client.v2.snapshots.bySnapshot_id(snapshotId).get(); + expect(getResp).toBeDefined(); + if (getResp) { + expect(getResp.snapshot?.name).toBe(expectedName); + } + if(!snapshotId){ + throw new Error(`Snapshot is not defined`); + } + const deleteResp = await client.v2.snapshots.bySnapshot_id(snapshotId).delete(); + expect(deleteResp).toBeUndefined(); + } finally { + if (volId) { + await client.v2.volumes.byVolume_id(volId).delete(); + } + } + }, 60000); +}); \ No newline at end of file diff --git a/tests/integration/sshKeys.test.js b/tests/integration/sshKeys.test.js new file mode 100644 index 000000000..526497af1 --- /dev/null +++ b/tests/integration/sshKeys.test.js @@ -0,0 +1,52 @@ +/** + * Integration tests for SSH keys. + */ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("SSH Keys API Integration Tests", () => { + it("should create, retrieve, update, and delete an SSH key", async () => { + const publicKey = process.env.PUBLIC_KEY; + const createResp = await client.v2.account.keys.post({ publicKey }); + expect(createResp).toBeDefined(); + const fingerprint = createResp?.sshKey?.fingerprint; + expect(fingerprint).toBeDefined(); + try { + if (!fingerprint) { + throw new Error(`fingerPrint Not Defined`); + } + const getByFingerprintResp = await client.v2.account.keys.bySsh_key_identifier(fingerprint).get(); + expect(getByFingerprintResp).toBeDefined(); + expect(getByFingerprintResp?.sshKey?.fingerprint).toBe(fingerprint); + const name = getByFingerprintResp?.sshKey?.name; + const keyId = getByFingerprintResp?.sshKey?.id; + expect(name).toBeDefined(); + expect(keyId).toBeDefined(); + if (!keyId) { + throw new Error(`fingerPrint Not Defined`); + } + const getByIdResp = await client.v2.account.keys.bySsh_key_identifier(keyId).get(); + expect(getByIdResp).toBeDefined(); + expect(getByIdResp?.sshKey?.fingerprint).toBe(fingerprint); + expect(getByIdResp?.sshKey?.name).toBe(name); + const newName = `${name}-updated`; + const updateResp = await client.v2.account.keys.bySsh_key_identifier(keyId.toString()).put({ name: newName }); + expect(updateResp).toBeDefined(); + expect(updateResp?.sshKey?.name).toBe(newName); + } + finally { + if (fingerprint) { + await client.v2.account.keys.bySsh_key_identifier(fingerprint).delete(); + } + } + }, 60000); +}); diff --git a/tests/integration/sshKeys.test.ts b/tests/integration/sshKeys.test.ts new file mode 100644 index 000000000..186abd1bd --- /dev/null +++ b/tests/integration/sshKeys.test.ts @@ -0,0 +1,56 @@ +/** + * Integration tests for SSH keys. + */ + +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("SSH Keys API Integration Tests", () => { + it("should create, retrieve, update, and delete an SSH key", async () => { + const publicKey = process.env.PUBLIC_KEY; + const createResp = await client.v2.account.keys.post({ publicKey }); + expect(createResp).toBeDefined(); + const fingerprint = createResp?.sshKey?.fingerprint; + expect(fingerprint).toBeDefined(); + + try { + if(!fingerprint){ + throw new Error(`fingerPrint Not Defined`); + } + const getByFingerprintResp = await client.v2.account.keys.bySsh_key_identifier(fingerprint).get(); + expect(getByFingerprintResp).toBeDefined(); + expect(getByFingerprintResp?.sshKey?.fingerprint).toBe(fingerprint); + const name = getByFingerprintResp?.sshKey?.name; + const keyId = getByFingerprintResp?.sshKey?.id; + expect(name).toBeDefined(); + expect(keyId).toBeDefined(); + if(!keyId){ + throw new Error(`fingerPrint Not Defined`); + } + const getByIdResp = await client.v2.account.keys.bySsh_key_identifier(keyId).get(); + expect(getByIdResp).toBeDefined(); + expect(getByIdResp?.sshKey?.fingerprint).toBe(fingerprint); + expect(getByIdResp?.sshKey?.name).toBe(name); + const newName = `${name}-updated`; + const updateResp = await client.v2.account.keys.bySsh_key_identifier(keyId.toString()).put({ name: newName }); + expect(updateResp).toBeDefined(); + expect(updateResp?.sshKey?.name).toBe(newName); + } finally { + if (fingerprint) { + await client.v2.account.keys.bySsh_key_identifier(fingerprint).delete(); + } + } + }, 60000); +}); \ No newline at end of file diff --git a/tests/integration/tags.test.js b/tests/integration/tags.test.js new file mode 100644 index 000000000..79c6fa4a2 --- /dev/null +++ b/tests/integration/tags.test.js @@ -0,0 +1,202 @@ +/** + * Integration tests for tags. + */ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +import { v4 as uuidv4 } from "uuid"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +const REGION = "nyc3"; +describe("Tags API Integration Tests", () => { + it("should create, retrieve, assign, and delete a tag", async () => { + const tagName = `test-dots-${uuidv4()}`; + const createBody = { name: tagName }; + const createTagResp = await client.v2.tags.post(createBody); + console.log(createTagResp); + expect(createTagResp).toBeDefined(); + expect(createTagResp?.tag?.name).toBe(tagName); + try { + const getTagResp = await client.v2.tags.byTag_id(tagName).get(); + console.log(getTagResp); + expect(getTagResp).toBeDefined(); + expect(getTagResp?.tag?.name).toBe(tagName); + expect(getTagResp?.tag?.resources?.count).toBe(0); + const keyName = process.env.SSH_KEY_NAME; + if (!keyName) { + throw new Error("SSH_KEY_NAME not set"); + } + const sshKey = await findSshKey(keyName); + const fingerprint = [sshKey.fingerprint ?? (() => { throw new Error("SSH key fingerprint is undefined or null"); })()]; + const dropletReq = { + name: `test-${uuidv4()}`, + region: REGION, + size: "s-1vcpu-1gb", + image: "ubuntu-22-04-x64", + ssh_keys: fingerprint, + }; + const droplet = await createDroplet(dropletReq); + expect(droplet).toBeDefined(); + if (!droplet?.id) { + throw new Error('Droplet not Created'); + } + const dropletId = droplet?.id; + expect(dropletId).toBeDefined(); + if (!dropletId) { + throw new Error("Droplet ID is not defined"); + } + await waitForDropletActive(dropletId); + const assignReq = { + resources: [ + { + resourceId: dropletId.toString(), + resourceType: "droplet", + }, + ], + }; + console.log(`TagName:`, tagName); + const assignTagResp = await client.v2.tags.byTag_id(tagName).resources.post(assignReq); + expect(assignTagResp).toBeUndefined(); + const getTagAfterAssignResp = await client.v2.tags.byTag_id(tagName).get(); + expect(getTagAfterAssignResp).toBeDefined(); + expect(getTagAfterAssignResp?.tag?.resources?.lastTaggedUri).toBe(`https://api.digitalocean.com/v2/droplets/${dropletId}`); + expect(getTagAfterAssignResp?.tag?.resources?.count).toBe(1); + expect(getTagAfterAssignResp?.tag?.resources?.droplets?.count).toBe(1); + } + finally { + await client.v2.tags.byTag_id(tagName).delete(); + } + }, 60000); +}); +async function waitForDropletActive(dropletId) { + let attempts = 0; + const maxAttempts = 30; + while (attempts < maxAttempts) { + const dropletResp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (dropletResp?.droplet?.status === "active") { + return; + } + await new Promise((resolve) => setTimeout(resolve, 10000)); + attempts++; + } + throw new Error(`Droplet ${dropletId} did not become active within the expected time`); +} +async function createDroplet(req) { + console.log(`Creating Droplet using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.droplets.post(req); + if (resp && 'droplet' in resp) { + const dropletId = resp.droplet?.id; + if (dropletId) { + let droplet; + let attempts = 0; + const maxAttempts = 10; + const delay = 5000; // 5 seconds + while (attempts < maxAttempts) { + const getResp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (getResp && 'droplet' in getResp) { + droplet = getResp.droplet; + if (droplet?.networks && droplet.networks.v4 && droplet.networks.v4.length > 0) { + break; + } + } + attempts++; + await new Promise(resolve => setTimeout(resolve, delay)); + } + if (droplet && droplet.networks && droplet.networks.v4) { + let ipAddress = ""; + for (const net of droplet.networks.v4) { + if (net?.type === "public" && net.ipAddress) { + ipAddress = net.ipAddress; + } + } + console.log(`Droplet ID: ${dropletId} Name: ${droplet.name} IP: ${ipAddress}`); + if (droplet?.id == null) { + throw new Error("Droplet ID is null or undefined"); + } + return { + id: droplet.id, + name: droplet.name, + region: droplet.region, + size: droplet.size, + image: droplet.image, + networks: { + v4: (droplet.networks?.v4 + ?.filter((net) => typeof net.ipAddress === "string" && !!net.type) + .map((net) => ({ + ip_address: net.ipAddress, + type: net.type, + }))) ?? [], + }, + }; + } + else { + throw new Error("Failed to retrieve droplet details or networks information"); + } + } + else { + throw new Error("Droplet ID is undefined"); + } + } + else { + throw new Error("Failed to create droplet"); + } + } + catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } + else { + throw err; + } + } +} +async function findSshKey(name) { + console.log(`Looking for SSH key named ${name}...`); + let paginated = true; + while (paginated) { + try { + const resp = await client.v2.account.keys.get(); + if (resp && resp.sshKeys) { + for (const k of resp.sshKeys) { + if (k.name === name) { + console.log(`Found SSH key: ${k.fingerprint}`); + return k; + } + } + const pages = resp.links?.pages; + if (pages && 'next' in pages) { + const nextUrl = pages.next; + if (nextUrl) { + const parsedUrl = new URL(nextUrl); + const nextPage = parseInt(parsedUrl.searchParams.get("page")); + console.log(`Next page: ${nextPage}`); + } + else { + paginated = false; + } + } + else { + console.log("No next page available"); + paginated = false; + } + } + else { + throw new Error("Failed to retrieve SSH keys"); + } + } + catch (err) { + console.error(`Error: ${err}`); + throw err; + } + } + throw new Error("No SSH key found"); +} diff --git a/tests/integration/tags.test.ts b/tests/integration/tags.test.ts new file mode 100644 index 000000000..01c0873d0 --- /dev/null +++ b/tests/integration/tags.test.ts @@ -0,0 +1,238 @@ +/** + * Integration tests for tags. + */ + +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +import { v4 as uuidv4 } from "uuid"; +import { Tags_resource } from "../../src/dots/models/index.js"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + + +const REGION = "nyc3"; +interface Droplet{ + id: number; + name: string; + region: string; + size: string; + image: string; + networks: { + v4: { ip_address: string; type: string }[]; + }; +} + +interface DropletRequest { + name: string; + region: string; + size: string; + image: string; + ssh_keys?: string[]; +} + +describe("Tags API Integration Tests", () => { + it("should create, retrieve, assign, and delete a tag", async () => { + const tagName = `test-dots-${uuidv4()}`; + const createBody = { name: tagName }; + const createTagResp = await client.v2.tags.post(createBody); + console.log(createTagResp); + expect(createTagResp).toBeDefined(); + expect(createTagResp?.tag?.name).toBe(tagName); + + + try { + const getTagResp = await client.v2.tags.byTag_id(tagName).get(); + console.log(getTagResp); + expect(getTagResp).toBeDefined(); + expect(getTagResp?.tag?.name).toBe(tagName); + expect(getTagResp?.tag?.resources?.count).toBe(0); + const keyName = process.env.SSH_KEY_NAME; + if (!keyName) { + throw new Error("SSH_KEY_NAME not set"); + } + const sshKey = await findSshKey(keyName); + const fingerprint : string[] = [sshKey.fingerprint ?? (() => { throw new Error("SSH key fingerprint is undefined or null"); })()] + const dropletReq :DropletRequest = { + name: `test-${uuidv4()}`, + region: REGION, + size: "s-1vcpu-1gb", + image: "ubuntu-22-04-x64", + ssh_keys:fingerprint, + }; + + const droplet: Droplet = await createDroplet(dropletReq); + expect(droplet).toBeDefined(); + if(!droplet?.id){ + throw new Error('Droplet not Created') + } + const dropletId = droplet?.id; + expect(dropletId).toBeDefined(); + if (!dropletId) { + throw new Error("Droplet ID is not defined"); + } + await waitForDropletActive(dropletId); + const assignReq : Tags_resource= { + resources: [ + { + resourceId: dropletId.toString(), + resourceType: "droplet", + }, + ], + }; + console.log(`TagName:`,tagName); + const assignTagResp = await client.v2.tags.byTag_id(tagName).resources.post(assignReq); + expect(assignTagResp).toBeUndefined(); + const getTagAfterAssignResp = await client.v2.tags.byTag_id(tagName).get(); + expect(getTagAfterAssignResp).toBeDefined(); + expect(getTagAfterAssignResp?.tag?.resources?.lastTaggedUri).toBe( + `https://api.digitalocean.com/v2/droplets/${dropletId}` + ); + expect(getTagAfterAssignResp?.tag?.resources?.count).toBe(1); + expect(getTagAfterAssignResp?.tag?.resources?.droplets?.count).toBe(1); + } finally { + await client.v2.tags.byTag_id(tagName).delete(); + } + }, 60000); +}); + +async function waitForDropletActive(dropletId: number): Promise { + let attempts = 0; + const maxAttempts = 30; + + while (attempts < maxAttempts) { + const dropletResp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (dropletResp?.droplet?.status === "active") { + return; + } + await new Promise((resolve) => setTimeout(resolve, 10000)); + attempts++; + } + + throw new Error(`Droplet ${dropletId} did not become active within the expected time`); +} + + +async function createDroplet(req: DropletRequest): Promise { + console.log(`Creating Droplet using: ${JSON.stringify(req)}`); + try { + const resp = await client.v2.droplets.post(req); + if (resp && 'droplet' in resp) { + const dropletId = resp.droplet?.id; + + if (dropletId) { + let droplet; + let attempts = 0; + const maxAttempts = 10; + const delay = 5000; // 5 seconds + + while (attempts < maxAttempts) { + const getResp = await client.v2.droplets.byDroplet_id(dropletId).get(); + if (getResp && 'droplet' in getResp) { + droplet = getResp.droplet; + if (droplet?.networks && droplet.networks.v4 && droplet.networks.v4.length > 0) { + break; + } + } + attempts++; + await new Promise(resolve => setTimeout(resolve, delay)); + } + + if (droplet && droplet.networks && droplet.networks.v4) { + let ipAddress = ""; + for (const net of droplet.networks.v4) { + if (net?.type === "public" && net.ipAddress) { + ipAddress = net.ipAddress; + } + } + console.log(`Droplet ID: ${dropletId} Name: ${droplet.name} IP: ${ipAddress}`); + if (droplet?.id == null) { + throw new Error("Droplet ID is null or undefined"); + } + return { + id: droplet.id, + name: droplet.name as string, + region: droplet.region as string, + size: droplet.size as string, + image: droplet.image as string, + networks: { + v4: (droplet.networks?.v4 + ?.filter((net: { ipAddress?: string | null; type?: string | null }) => + typeof net.ipAddress === "string" && !!net.type + ) + .map((net: { ipAddress?: string | null; type?: string | null }) => ({ + ip_address: net.ipAddress as string, + type: net.type as string, + })) + ) ?? [], + }, + }; + } else { + throw new Error("Failed to retrieve droplet details or networks information"); + } + } else { + throw new Error("Droplet ID is undefined"); + } + } else { + throw new Error("Failed to create droplet"); + } + + } catch (err) { + if (err instanceof Error && 'statusCode' in err) { + const httpError = err as Error & { + statusCode: number; + response?: { bodyAsText?: string } + }; + throw new Error(`Error: ${httpError.statusCode} ${httpError.message}: ${httpError.response?.bodyAsText}`); + } else { + throw err; + } + } +} + +async function findSshKey(name: string): Promise<{ name?: string | null; fingerprint?: string | null }> { + console.log(`Looking for SSH key named ${name}...`); + let paginated = true; + while (paginated) { + try { + const resp = await client.v2.account.keys.get(); + if (resp && resp.sshKeys) { + for (const k of resp.sshKeys) { + if (k.name === name) { + console.log(`Found SSH key: ${k.fingerprint}`); + return k; + } + } + const pages = resp.links?.pages; + if (pages && 'next' in pages) { + const nextUrl = pages.next; + if (nextUrl) { + const parsedUrl = new URL(nextUrl); + const nextPage = parseInt(parsedUrl.searchParams.get("page")!); + console.log(`Next page: ${nextPage}`); + } else { + paginated = false; + } + } else { + console.log("No next page available"); + paginated = false; + } + } else { + throw new Error("Failed to retrieve SSH keys"); + } + } catch (err) { + console.error(`Error: ${err}`); + throw err; + } + } + throw new Error("No SSH key found"); +} diff --git a/tests/integration/vpc.test.js b/tests/integration/vpc.test.js new file mode 100644 index 000000000..5ff10c68f --- /dev/null +++ b/tests/integration/vpc.test.js @@ -0,0 +1,123 @@ +/** + * Integration tests for VPCs + */ +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +import { v4 as uuidv4 } from "uuid"; +dotenv.config(); +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +const PREFIX = "test"; +const REGION = "nyc3"; +describe("VPCs API Integration Tests", () => { + it("should create and delete a VPC", async () => { + const expectedName = `${PREFIX}-${uuidv4()}`; + const createReq = { + name: expectedName, + description: "VPC for testing client gen", + region: REGION, + }; + let vpcId; + try { + const createResp = await client.v2.vpcs.post(createReq); + expect(createResp).toBeDefined(); + expect(createResp?.vpc?.name).toBe(expectedName); + vpcId = createResp?.vpc?.id ?? ''; + expect(vpcId).toBeDefined(); + } + finally { + if (vpcId) { + await client.v2.vpcs.byVpc_id(vpcId).delete(); + } + } + }); + it("should list all VPCs", async () => { + const listResp = await client.v2.vpcs.get(); + expect(listResp).toBeDefined(); + expect(listResp?.vpcs?.length).toBeGreaterThan(0); + }); + it("should retrieve a VPC by ID", async () => { + const expectedName = `${PREFIX}-${uuidv4()}`; + const createReq = { + name: expectedName, + description: "VPC for testing client gen", + region: REGION, + }; + let vpcId; + try { + const createResp = await client.v2.vpcs.post(createReq); + expect(createResp).toBeDefined(); + vpcId = createResp?.vpc?.id ?? ''; + expect(vpcId).toBeDefined(); + if (!vpcId) { + throw new Error(`VpcId not Defined`); + } + const getResp = await client.v2.vpcs.byVpc_id(vpcId).get(); + expect(getResp).toBeDefined(); + expect(getResp?.vpc?.id).toBe(vpcId); + } + finally { + if (vpcId) { + await client.v2.vpcs.byVpc_id(vpcId).delete(); + } + } + }); + it("should update a VPC", async () => { + const initialName = `${PREFIX}-${uuidv4()}`; + const updatedName = `${PREFIX}-${uuidv4()}`; + const createReq = { + name: initialName, + description: "VPC for testing client gen", + region: REGION, + }; + let vpcId; + try { + const createResp = await client.v2.vpcs.post(createReq); + expect(createResp).toBeDefined(); + vpcId = createResp?.vpc?.id ?? ''; + expect(vpcId).toBeDefined(); + const updateReq = { name: updatedName }; + const updateResp = await client.v2.vpcs.byVpc_id(vpcId).put(updateReq); + expect(updateResp).toBeDefined(); + expect(updateResp?.vpc?.name).toBe(updatedName); + } + finally { + if (vpcId) { + await client.v2.vpcs.byVpc_id(vpcId).delete(); + } + } + }); + it("should patch a VPC (partial update)", async () => { + const initialName = `${PREFIX}-${uuidv4()}`; + const updatedName = `${PREFIX}-${uuidv4()}`; + const createReq = { + name: initialName, + description: "VPC for testing client gen", + region: REGION, + }; + let vpcId; + try { + const createResp = await client.v2.vpcs.post(createReq); + expect(createResp).toBeDefined(); + vpcId = createResp?.vpc?.id ?? ''; + expect(vpcId).toBeDefined(); + const patchReq = { name: updatedName }; + const patchResp = await client.v2.vpcs.byVpc_id(vpcId).patch(patchReq); + expect(patchResp).toBeDefined(); + expect(patchResp?.vpc?.name).toBe(updatedName); + expect(patchResp?.vpc?.description).toBe(createReq.description); + } + finally { + if (vpcId) { + await client.v2.vpcs.byVpc_id(vpcId).delete(); + } + } + }); +}); diff --git a/tests/integration/vpc.test.ts b/tests/integration/vpc.test.ts new file mode 100644 index 000000000..977a49f05 --- /dev/null +++ b/tests/integration/vpc.test.ts @@ -0,0 +1,139 @@ +/** + * Integration tests for VPCs + */ + +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import dotenv from "dotenv"; +import { v4 as uuidv4 } from "uuid"; + +dotenv.config(); + +const token = process.env.DIGITALOCEAN_TOKEN; +if (!token) { + throw new Error("DIGITALOCEAN_TOKEN is not set. Please check your .env file."); +} +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +const PREFIX = "test"; +const REGION = "nyc3"; + +describe("VPCs API Integration Tests", () => { + it("should create and delete a VPC", async () => { + const expectedName = `${PREFIX}-${uuidv4()}`; + const createReq = { + name: expectedName, + description: "VPC for testing client gen", + region: REGION, + }; + + let vpcId: string | undefined; + + try { + const createResp = await client.v2.vpcs.post(createReq); + expect(createResp).toBeDefined(); + expect(createResp?.vpc?.name).toBe(expectedName); + vpcId = createResp?.vpc?.id ?? ''; + expect(vpcId).toBeDefined(); + } finally { + if (vpcId) { + await client.v2.vpcs.byVpc_id(vpcId).delete(); + } + } + }); + + it("should list all VPCs", async () => { + const listResp = await client.v2.vpcs.get(); + expect(listResp).toBeDefined(); + expect(listResp?.vpcs?.length).toBeGreaterThan(0); + }); + + it("should retrieve a VPC by ID", async () => { + const expectedName = `${PREFIX}-${uuidv4()}`; + const createReq = { + name: expectedName, + description: "VPC for testing client gen", + region: REGION, + }; + + let vpcId: string | undefined; + + try { + const createResp = await client.v2.vpcs.post(createReq); + expect(createResp).toBeDefined(); + vpcId = createResp?.vpc?.id ?? ''; + expect(vpcId).toBeDefined(); + if(!vpcId){ + throw new Error(`VpcId not Defined`); + } + const getResp = await client.v2.vpcs.byVpc_id(vpcId).get(); + expect(getResp).toBeDefined(); + expect(getResp?.vpc?.id).toBe(vpcId); + } finally { + if (vpcId) { + await client.v2.vpcs.byVpc_id(vpcId).delete(); + } + } + }); + + it("should update a VPC", async () => { + const initialName = `${PREFIX}-${uuidv4()}`; + const updatedName = `${PREFIX}-${uuidv4()}`; + const createReq = { + name: initialName, + description: "VPC for testing client gen", + region: REGION, + }; + + let vpcId: string | undefined; + + try { + const createResp = await client.v2.vpcs.post(createReq); + expect(createResp).toBeDefined(); + vpcId = createResp?.vpc?.id ?? ''; + expect(vpcId).toBeDefined(); + + const updateReq = { name: updatedName }; + const updateResp = await client.v2.vpcs.byVpc_id(vpcId).put(updateReq); + expect(updateResp).toBeDefined(); + expect(updateResp?.vpc?.name).toBe(updatedName); + } finally { + if (vpcId) { + await client.v2.vpcs.byVpc_id(vpcId).delete(); + } + } + }); + + it("should patch a VPC (partial update)", async () => { + const initialName = `${PREFIX}-${uuidv4()}`; + const updatedName = `${PREFIX}-${uuidv4()}`; + const createReq = { + name: initialName, + description: "VPC for testing client gen", + region: REGION, + }; + + let vpcId: string | undefined; + + try { + const createResp = await client.v2.vpcs.post(createReq); + expect(createResp).toBeDefined(); + vpcId = createResp?.vpc?.id ?? ''; + expect(vpcId).toBeDefined(); + + const patchReq = { name: updatedName }; + const patchResp = await client.v2.vpcs.byVpc_id(vpcId).patch(patchReq); + expect(patchResp).toBeDefined(); + expect(patchResp?.vpc?.name).toBe(updatedName); + expect(patchResp?.vpc?.description).toBe(createReq.description); + } finally { + if (vpcId) { + await client.v2.vpcs.byVpc_id(vpcId).delete(); + } + } + }); +}); + diff --git a/tests/mocked/account.test.js b/tests/mocked/account.test.js index ba735bc65..a0424c253 100644 --- a/tests/mocked/account.test.js +++ b/tests/mocked/account.test.js @@ -51,11 +51,4 @@ describe('Account API Resource', () => { const account = await client.v2.account.get(); expect(account).toEqual(EXPECTED_ACCOUNT_RESPONSE); }); - // it('should mock the account get operation (async)', async () => { - // nock(baseUrl) - // .get('/v2/account') - // .reply(200, EXPECTED_ACCOUNT); - // const account = await client.v2.account.get(); - // expect(account).toEqual(EXPECTED_ACCOUNT); - // }); }); diff --git a/tests/mocked/account.test.ts b/tests/mocked/account.test.ts index 373ef6a59..8221bafe6 100644 --- a/tests/mocked/account.test.ts +++ b/tests/mocked/account.test.ts @@ -60,14 +60,4 @@ describe('Account API Resource', () => { expect(account).toEqual(EXPECTED_ACCOUNT_RESPONSE); }); - -// it('should mock the account get operation (async)', async () => { -// nock(baseUrl) -// .get('/v2/account') -// .reply(200, EXPECTED_ACCOUNT); - -// const account = await client.v2.account.get(); - -// expect(account).toEqual(EXPECTED_ACCOUNT); -// }); }); \ No newline at end of file diff --git a/tests/mocked/app.test.js b/tests/mocked/app.test.js index 879e09fb3..3b30b0de0 100644 --- a/tests/mocked/app.test.js +++ b/tests/mocked/app.test.js @@ -66,7 +66,6 @@ describe('Apps API Resource - Create App', () => { ], }, }; - // Mock the POST request const requestBodyNock = { spec: { name: "web-app", @@ -85,9 +84,7 @@ describe('Apps API Resource - Create App', () => { }, }; nock(baseUrl).post("/v2/apps", requestBodyNock).reply(200, expected); - // Call the create method const createResp = await client.v2.apps.post(requestBody); - // Assert the response expect(createResp).toEqual(typeExpected); }); it("should mock the list of apps", async () => { @@ -101,8 +98,6 @@ describe('Apps API Resource - Create App', () => { created_at: "2020-11-19T20:27:18Z", updated_at: "2020-12-01T00:42:16Z", active_deployment: {}, - // cause: "app spec updated", - // progress: {}, last_deployment_created_at: "2020-12-01T00:40:05Z", live_url: "https://sample-php-iaj87.ondigitalocean.app", region: {}, @@ -124,8 +119,6 @@ describe('Apps API Resource - Create App', () => { createdAt: new Date("2020-11-19T20:27:18Z"), updatedAt: new Date("2020-12-01T00:42:16Z"), activeDeployment: {}, - // cause: "app spec updated", - // progress: {}, lastDeploymentCreatedAt: new Date("2020-12-01T00:40:05Z"), liveUrl: "https://sample-php-iaj87.ondigitalocean.app", region: {}, @@ -241,11 +234,8 @@ describe('Apps API Resource - Create App', () => { }, app_id: "b6bdf840-2854-4f87-a36c-5f231c617c84", }; - // Mock the POST request nock(baseUrl).post("/v2/apps/propose", proposedSpecNock).reply(200, expected); - // Call the validate_app_spec method const proposeResp = await client.v2.apps.propose.post(proposedSpec); - // Assert the response expect(proposeResp).toEqual(typeExpected); }); it("should mock listing alerts for an app", async () => { @@ -332,11 +322,8 @@ describe('Apps API Resource - Create App', () => { }, ], }; - // Mock the GET request nock(baseUrl).get(`/v2/apps/${appId}/alerts`).reply(200, expected); - // Call the list_alerts method const listResp = await client.v2.apps.byApp_Id(appId).alerts.get(); - // Assert the response expect(listResp).toEqual(typeExpected); }); it("should mock changing alert destinations", async () => { @@ -618,205 +605,6 @@ describe('Apps API Resource - Create App', () => { const resp = await client.v2.apps.regions.get(); expect(resp).toEqual(typeExpected); }); - // it("should mock creating a rollback", async () => { - // const appId = "1"; - // const expected = { - // deployment: { - // id: "2", - // spec: { - // name: "sample-golang", - // services: [ - // { - // name: "web", - // github: { - // repo: "digitalocean/sample-golang", - // branch: "branch", - // }, - // run_command: "bin/sample-golang", - // environment_slug: "go", - // instance_size_slug: "basic-xxs", - // instance_count: 2, - // routes: [{ path: "/" }], - // }, - // ], - // region: "ams", - // }, - // services: [ - // { - // name: "web", - // source_commit_hash: "9a4df0b8e161e323bc3cdf1dc71878080fe144fa", - // }, - // ], - // phase_last_updated_at: "0001-01-01T00:00:00Z", - // created_at: "2020-07-28T18:00:00Z", - // updated_at: "2020-07-28T18:00:00Z", - // cause: "commit 9a4df0b pushed to github/digitalocean/sample-golang", - // progress: { - // pending_steps: 6, - // total_steps: 6, - // steps: [ - // { - // name: "build", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // component_name: "web", - // message_base: "Building service", - // }, - // ], - // }, - // ], - // }, - // { - // name: "deploy", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // steps: [ - // { - // name: "deploy", - // status: "PENDING", - // component_name: "web", - // message_base: "Deploying service", - // }, - // { - // name: "wait", - // status: "PENDING", - // component_name: "web", - // message_base: "Waiting for service", - // }, - // ], - // component_name: "web", - // }, - // ], - // }, - // { name: "finalize", status: "PENDING" }, - // ], - // }, - // ], - // }, - // phase: "PENDING_BUILD", - // tier_slug: "basic", - // }, - // }; - // const typeExpected = { - // deployment: { - // id: "2", - // spec: { - // name: "sample-golang", - // services: [ - // { - // name: "web", - // github: { - // repo: "digitalocean/sample-golang", - // branch: "branch", - // }, - // runCommand: "bin/sample-golang", - // environmentSlug: "go", - // instanceSizeSlug: "basic-xxs", - // instanceCount: 2, - // routes: [{ path: "/" }], - // }, - // ], - // region: "ams", - // }, - // services: [ - // { - // name: "web", - // sourceCommitHash: "9a4df0b8e161e323bc3cdf1dc71878080fe144fa", - // }, - // ], - // phaseLastUpdatedAt: new Date("0001-01-01T00:00:00Z"), - // createdAt: new Date("2020-07-28T18:00:00Z"), - // updatedAt: new Date("2020-07-28T18:00:00Z"), - // cause: "commit 9a4df0b pushed to github/digitalocean/sample-golang", - // progress: { - // pendingSteps: 6, - // totalSteps: 6, - // steps: [ - // { - // name: "build", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // componentName: "web", - // messageBase: "Building service", - // }, - // ], - // }, - // ], - // }, - // { - // name: "deploy", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // steps: [ - // { - // name: "deploy", - // status: "PENDING", - // componentName: "web", - // messageBase: "Deploying service", - // }, - // { - // name: "wait", - // status: "PENDING", - // componentName: "web", - // messageBase: "Waiting for service", - // }, - // ], - // componentName: "web", - // }, - // ], - // }, - // { name: "finalize", status: "PENDING" }, - // ], - // }, - // ], - // }, - // phase: "PENDING_BUILD", - // tierSlug: "basic", - // }, - // }; - // const postReqNock = { deployment_id: "2", skip_pin: false }; - // const postReq : Apps_rollback_app_request = { deploymentId: "2", skipPin: false }; - // // Mock the POST request - // nock(baseUrl) - // .post(`/v2/apps/${appId}/rollback`, postReqNock) - // .reply(200, expected); - // // Call the create_rollback method - // const resp = await client.v2.apps.byApp_Id(appId).rollback.post(postReq); - // // Assert the response - // expect(resp).toEqual(typeExpected); - // }); it("should mock validating a rollback", async () => { const appId = "1"; const expected = { valid: true }; @@ -834,198 +622,6 @@ describe('Apps API Resource - Create App', () => { const commitResp = await client.v2.apps.byApp_Id(appId).rollback.commit.post(); expect(JSON.stringify(commitResp)).toEqual(JSON.stringify({})); }); - // it("should mock reverting a rollback", async () => { - // const appId = "1"; - // const expected = { - // deployment: { - // id: "b6bdf840-2854-4f87-a36c-5f231c617c84", - // spec: { - // name: "sample-golang", - // services: [ - // { - // name: "web", - // github: { - // repo: "digitalocean/sample-golang", - // branch: "branch", - // }, - // run_command: "bin/sample-golang", - // environment_slug: "go", - // instance_size_slug: "basic-xxs", - // instance_count: 2, - // routes: [{ path: "/" }], - // }, - // ], - // region: "ams", - // }, - // services: [ - // { - // name: "web", - // source_commit_hash: "9a4df0b8e161e323bc3cdf1dc71878080fe144fa", - // }, - // ], - // phase_last_updated_at: "0001-01-01T00:00:00Z", - // created_at: "2020-07-28T18:00:00Z", - // updated_at: "2020-07-28T18:00:00Z", - // cause: "commit 9a4df0b pushed to github/digitalocean/sample-golang", - // progress: { - // pending_steps: 6, - // total_steps: 6, - // steps: [ - // { - // name: "build", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // component_name: "web", - // message_base: "Building service", - // }, - // ], - // }, - // ], - // }, - // { - // name: "deploy", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // steps: [ - // { - // name: "deploy", - // status: "PENDING", - // component_name: "web", - // message_base: "Deploying service", - // }, - // { - // name: "wait", - // status: "PENDING", - // component_name: "web", - // message_base: "Waiting for service", - // }, - // ], - // component_name: "web", - // }, - // ], - // }, - // { name: "finalize", status: "PENDING" }, - // ], - // }, - // ], - // }, - // phase: "PENDING_BUILD", - // tier_slug: "basic", - // }, - // }; - // const typeExpected = { - // deployment: { - // id: "b6bdf840-2854-4f87-a36c-5f231c617c84", - // spec: { - // name: "sample-golang", - // services: [ - // { - // name: "web", - // github: { - // repo: "digitalocean/sample-golang", - // branch: "branch", - // }, - // runCommand: "bin/sample-golang", - // environmentSlug: "go", - // instanceSizeSlug: "basic-xxs", - // instanceCount: 2, - // routes: [{ path: "/" }], - // }, - // ], - // region: "ams", - // }, - // services: [ - // { - // name: "web", - // sourceCommitHash: "9a4df0b8e161e323bc3cdf1dc71878080fe144fa", - // }, - // ], - // phaseLastUpdated_at: "0001-01-01T00:00:00Z", - // createdAt: "2020-07-28T18:00:00Z", - // updatedAt: "2020-07-28T18:00:00Z", - // cause: "commit 9a4df0b pushed to github/digitalocean/sample-golang", - // progress: { - // pendingSteps: 6, - // totalSteps: 6, - // steps: [ - // { - // name: "build", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // component_name: "web", - // message_base: "Building service", - // }, - // ], - // }, - // ], - // }, - // { - // name: "deploy", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // steps: [ - // { - // name: "deploy", - // status: "PENDING", - // component_name: "web", - // message_base: "Deploying service", - // }, - // { - // name: "wait", - // status: "PENDING", - // component_name: "web", - // message_base: "Waiting for service", - // }, - // ], - // component_name: "web", - // }, - // ], - // }, - // { name: "finalize", status: "PENDING" }, - // ], - // }, - // ], - // }, - // phase: "PENDING_BUILD", - // tier_slug: "basic", - // }, - // }; - // nock(baseUrl).post(`/v2/apps/${appId}/rollback/revert`).reply(200, expected); - // const resp = await client.v2.apps.byApp_Id(appId).rollback.revert.post(); - // expect(resp).toEqual(typeExpected); - // }); it("should mock retrieving bandwidth daily metrics for multiple apps", async () => { const typeExpected = { appBandwidthUsage: [ @@ -1065,7 +661,7 @@ describe('Apps API Resource - Create App', () => { '4f6c71e2-1e90-4762-9fee-6cc4a0a9f2cf', 'c2a93513-8d9b-4223-9d61-5f231c617c84', ], - date: '2023-01-17T00:00:00.000Z', // Ensure this matches the request + date: '2023-01-17T00:00:00.000Z', }; nock(baseUrl) .post('/v2/apps/metrics/bandwidth_daily', reqNock) diff --git a/tests/mocked/app.test.ts b/tests/mocked/app.test.ts index 164935d53..f15297d49 100644 --- a/tests/mocked/app.test.ts +++ b/tests/mocked/app.test.ts @@ -73,8 +73,6 @@ describe('Apps API Resource - Create App', () => { ], }, }; - - // Mock the POST request const requestBodyNock = { spec: { name: "web-app", @@ -94,11 +92,7 @@ describe('Apps API Resource - Create App', () => { }; nock(baseUrl).post("/v2/apps", requestBodyNock).reply(200, expected); - - // Call the create method const createResp = await client.v2.apps.post(requestBody); - - // Assert the response expect(createResp).toEqual(typeExpected); }); @@ -113,8 +107,6 @@ describe('Apps API Resource - Create App', () => { created_at: "2020-11-19T20:27:18Z", updated_at: "2020-12-01T00:42:16Z", active_deployment: {}, - // cause: "app spec updated", - // progress: {}, last_deployment_created_at: "2020-12-01T00:40:05Z", live_url: "https://sample-php-iaj87.ondigitalocean.app", region: {}, @@ -137,8 +129,6 @@ describe('Apps API Resource - Create App', () => { createdAt: new Date("2020-11-19T20:27:18Z"), updatedAt: new Date("2020-12-01T00:42:16Z"), activeDeployment: {}, - // cause: "app spec updated", - // progress: {}, lastDeploymentCreatedAt: new Date("2020-12-01T00:40:05Z"), liveUrl: "https://sample-php-iaj87.ondigitalocean.app", region: {}, @@ -259,14 +249,8 @@ describe('Apps API Resource - Create App', () => { }, app_id: "b6bdf840-2854-4f87-a36c-5f231c617c84", }; - - // Mock the POST request nock(baseUrl).post("/v2/apps/propose", proposedSpecNock).reply(200, expected); - - // Call the validate_app_spec method const proposeResp = await client.v2.apps.propose.post(proposedSpec); - - // Assert the response expect(proposeResp).toEqual(typeExpected); }); @@ -355,14 +339,8 @@ describe('Apps API Resource - Create App', () => { }, ], }; - - // Mock the GET request nock(baseUrl).get(`/v2/apps/${appId}/alerts`).reply(200, expected); - - // Call the list_alerts method const listResp = await client.v2.apps.byApp_Id(appId).alerts.get(); - - // Assert the response expect(listResp).toEqual(typeExpected); }); @@ -656,213 +634,6 @@ describe('Apps API Resource - Create App', () => { expect(resp).toEqual(typeExpected); }); - // it("should mock creating a rollback", async () => { - // const appId = "1"; - // const expected = { - // deployment: { - // id: "2", - // spec: { - // name: "sample-golang", - // services: [ - // { - // name: "web", - // github: { - // repo: "digitalocean/sample-golang", - // branch: "branch", - // }, - // run_command: "bin/sample-golang", - // environment_slug: "go", - // instance_size_slug: "basic-xxs", - // instance_count: 2, - // routes: [{ path: "/" }], - // }, - // ], - // region: "ams", - // }, - // services: [ - // { - // name: "web", - // source_commit_hash: "9a4df0b8e161e323bc3cdf1dc71878080fe144fa", - // }, - // ], - // phase_last_updated_at: "0001-01-01T00:00:00Z", - // created_at: "2020-07-28T18:00:00Z", - // updated_at: "2020-07-28T18:00:00Z", - // cause: "commit 9a4df0b pushed to github/digitalocean/sample-golang", - // progress: { - // pending_steps: 6, - // total_steps: 6, - // steps: [ - // { - // name: "build", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // component_name: "web", - // message_base: "Building service", - // }, - // ], - // }, - // ], - // }, - // { - // name: "deploy", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // steps: [ - // { - // name: "deploy", - // status: "PENDING", - // component_name: "web", - // message_base: "Deploying service", - // }, - // { - // name: "wait", - // status: "PENDING", - // component_name: "web", - // message_base: "Waiting for service", - // }, - // ], - // component_name: "web", - // }, - // ], - // }, - // { name: "finalize", status: "PENDING" }, - // ], - // }, - // ], - // }, - // phase: "PENDING_BUILD", - // tier_slug: "basic", - // }, - // }; - - // const typeExpected = { - // deployment: { - // id: "2", - // spec: { - // name: "sample-golang", - // services: [ - // { - // name: "web", - // github: { - // repo: "digitalocean/sample-golang", - // branch: "branch", - // }, - // runCommand: "bin/sample-golang", - // environmentSlug: "go", - // instanceSizeSlug: "basic-xxs", - // instanceCount: 2, - // routes: [{ path: "/" }], - // }, - // ], - // region: "ams", - // }, - // services: [ - // { - // name: "web", - // sourceCommitHash: "9a4df0b8e161e323bc3cdf1dc71878080fe144fa", - // }, - // ], - // phaseLastUpdatedAt: new Date("0001-01-01T00:00:00Z"), - // createdAt: new Date("2020-07-28T18:00:00Z"), - // updatedAt: new Date("2020-07-28T18:00:00Z"), - // cause: "commit 9a4df0b pushed to github/digitalocean/sample-golang", - // progress: { - // pendingSteps: 6, - // totalSteps: 6, - // steps: [ - // { - // name: "build", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // componentName: "web", - // messageBase: "Building service", - // }, - // ], - // }, - // ], - // }, - // { - // name: "deploy", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // steps: [ - // { - // name: "deploy", - // status: "PENDING", - // componentName: "web", - // messageBase: "Deploying service", - // }, - // { - // name: "wait", - // status: "PENDING", - // componentName: "web", - // messageBase: "Waiting for service", - // }, - // ], - // componentName: "web", - // }, - // ], - // }, - // { name: "finalize", status: "PENDING" }, - // ], - // }, - // ], - // }, - // phase: "PENDING_BUILD", - // tierSlug: "basic", - // }, - // }; - - // const postReqNock = { deployment_id: "2", skip_pin: false }; - - // const postReq : Apps_rollback_app_request = { deploymentId: "2", skipPin: false }; - - - // // Mock the POST request - // nock(baseUrl) - // .post(`/v2/apps/${appId}/rollback`, postReqNock) - // .reply(200, expected); - - // // Call the create_rollback method - // const resp = await client.v2.apps.byApp_Id(appId).rollback.post(postReq); - - // // Assert the response - // expect(resp).toEqual(typeExpected); - // }); - it("should mock validating a rollback", async () => { const appId = "1"; const expected = { valid: true }; @@ -885,202 +656,6 @@ describe('Apps API Resource - Create App', () => { expect(JSON.stringify(commitResp)).toEqual(JSON.stringify({})); }); - // it("should mock reverting a rollback", async () => { - // const appId = "1"; - // const expected = { - // deployment: { - // id: "b6bdf840-2854-4f87-a36c-5f231c617c84", - // spec: { - // name: "sample-golang", - // services: [ - // { - // name: "web", - // github: { - // repo: "digitalocean/sample-golang", - // branch: "branch", - // }, - // run_command: "bin/sample-golang", - // environment_slug: "go", - // instance_size_slug: "basic-xxs", - // instance_count: 2, - // routes: [{ path: "/" }], - // }, - // ], - // region: "ams", - // }, - // services: [ - // { - // name: "web", - // source_commit_hash: "9a4df0b8e161e323bc3cdf1dc71878080fe144fa", - // }, - // ], - // phase_last_updated_at: "0001-01-01T00:00:00Z", - // created_at: "2020-07-28T18:00:00Z", - // updated_at: "2020-07-28T18:00:00Z", - // cause: "commit 9a4df0b pushed to github/digitalocean/sample-golang", - // progress: { - // pending_steps: 6, - // total_steps: 6, - // steps: [ - // { - // name: "build", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // component_name: "web", - // message_base: "Building service", - // }, - // ], - // }, - // ], - // }, - // { - // name: "deploy", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // steps: [ - // { - // name: "deploy", - // status: "PENDING", - // component_name: "web", - // message_base: "Deploying service", - // }, - // { - // name: "wait", - // status: "PENDING", - // component_name: "web", - // message_base: "Waiting for service", - // }, - // ], - // component_name: "web", - // }, - // ], - // }, - // { name: "finalize", status: "PENDING" }, - // ], - // }, - // ], - // }, - // phase: "PENDING_BUILD", - // tier_slug: "basic", - // }, - // }; - - // const typeExpected = { - // deployment: { - // id: "b6bdf840-2854-4f87-a36c-5f231c617c84", - // spec: { - // name: "sample-golang", - // services: [ - // { - // name: "web", - // github: { - // repo: "digitalocean/sample-golang", - // branch: "branch", - // }, - // runCommand: "bin/sample-golang", - // environmentSlug: "go", - // instanceSizeSlug: "basic-xxs", - // instanceCount: 2, - // routes: [{ path: "/" }], - // }, - // ], - // region: "ams", - // }, - // services: [ - // { - // name: "web", - // sourceCommitHash: "9a4df0b8e161e323bc3cdf1dc71878080fe144fa", - // }, - // ], - // phaseLastUpdated_at: "0001-01-01T00:00:00Z", - // createdAt: "2020-07-28T18:00:00Z", - // updatedAt: "2020-07-28T18:00:00Z", - // cause: "commit 9a4df0b pushed to github/digitalocean/sample-golang", - // progress: { - // pendingSteps: 6, - // totalSteps: 6, - // steps: [ - // { - // name: "build", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // component_name: "web", - // message_base: "Building service", - // }, - // ], - // }, - // ], - // }, - // { - // name: "deploy", - // status: "PENDING", - // steps: [ - // { name: "initialize", status: "PENDING" }, - // { - // name: "components", - // status: "PENDING", - // steps: [ - // { - // name: "web", - // status: "PENDING", - // steps: [ - // { - // name: "deploy", - // status: "PENDING", - // component_name: "web", - // message_base: "Deploying service", - // }, - // { - // name: "wait", - // status: "PENDING", - // component_name: "web", - // message_base: "Waiting for service", - // }, - // ], - // component_name: "web", - // }, - // ], - // }, - // { name: "finalize", status: "PENDING" }, - // ], - // }, - // ], - // }, - // phase: "PENDING_BUILD", - // tier_slug: "basic", - // }, - // }; - - - // nock(baseUrl).post(`/v2/apps/${appId}/rollback/revert`).reply(200, expected); - // const resp = await client.v2.apps.byApp_Id(appId).rollback.revert.post(); - // expect(resp).toEqual(typeExpected); - // }); - it("should mock retrieving bandwidth daily metrics for multiple apps", async () => { const typeExpected = { appBandwidthUsage: [ @@ -1122,7 +697,7 @@ describe('Apps API Resource - Create App', () => { '4f6c71e2-1e90-4762-9fee-6cc4a0a9f2cf', 'c2a93513-8d9b-4223-9d61-5f231c617c84', ], - date: '2023-01-17T00:00:00.000Z', // Ensure this matches the request + date: '2023-01-17T00:00:00.000Z', }; nock(baseUrl) diff --git a/tests/mocked/billling.test.js b/tests/mocked/billling.test.js index 1a4b56b5c..46c0f26ba 100644 --- a/tests/mocked/billling.test.js +++ b/tests/mocked/billling.test.js @@ -131,71 +131,6 @@ describe('Billing API Resource Tests', () => { const balance = await client.v2.customers.my.invoices.get(); expect(balance).toEqual(typeExpected); }); - // it('should mock GET a list of invoices with pagination', async () => { - // const expected = { - // invoices: [ - // { - // invoice_uuid: '22737513-0ea7-4206-8ceb-98a575af7681', - // amount: '12.34', - // invoice_period: '2019-12', - // }, - // { - // invoice_uuid: 'fdabb512-6faf-443c-ba2e-665452332a9e', - // amount: '23.45', - // invoice_period: '2019-11', - // }, - // ], - // invoice_preview: { - // invoice_uuid: '1afe95e6-0958-4eb0-8d9a-9c5060d3ef03', - // amount: '34.56', - // invoice_period: '2020-02', - // updated_at: '2020-02-23T06:31:50Z', - // }, - // links: { - // pages: { - // next: 'https://api.digitalocean.com/v2/customers/my/invoices?page=2&per_page=20', - // last: 'https://api.digitalocean.com/v2/customers/my/invoices?page=6&per_page=20', - // }, - // }, - // meta: { total: 6 }, - // }; - // const typeExpected = { - // invoices: [ - // { - // invoiceUuid: '22737513-0ea7-4206-8ceb-98a575af7681', - // amount: '12.34', - // invoicePeriod: '2019-12', - // }, - // { - // invoiceUuid: 'fdabb512-6faf-443c-ba2e-665452332a9e', - // amount: '23.45', - // invoicePeriod: '2019-11', - // }, - // ], - // invoicePreview: { - // invoiceUuid: '1afe95e6-0958-4eb0-8d9a-9c5060d3ef03', - // amount: '34.56', - // invoicePeriod: '2020-02', - // updatedAt: '2020-02-23T06:31:50Z', - // }, - // links: { - // pages: { - // next: 'https://api.digitalocean.com/v2/customers/my/invoices?page=2&per_page=20', - // last: 'https://api.digitalocean.com/v2/customers/my/invoices?page=6&per_page=20', - // }, - // }, - // meta: { total: 6 }, - // }; - // const params = { per_page: 20, page: 1 }; - // nock(baseUrl) - // .get('/v2/customers/my/invoices') - // .query(params) - // .reply(200, expected); - // const balance = await client.v2.customers.my.invoices.get({ - // queryParameters: params - // }); - // expect(balance).toEqual(typeExpected); - // }); it('should mock GET invoice by UUID', async () => { const expected = { invoice_items: [ @@ -275,7 +210,6 @@ describe('Billing API Resource Tests', () => { .get('/v2/customers/my/invoices/1/pdf') .reply(200, expected); const invoices = await client.v2.customers.my.invoices.byInvoice_uuid("1").pdf.get(); - // const listIn = Array.from(invoices); if (invoices) { const decodedInvoice = new TextDecoder('utf-8').decode(invoices); expect(decodedInvoice).toEqual(expected); @@ -283,7 +217,6 @@ describe('Billing API Resource Tests', () => { else { throw new Error('The balance response is undefined'); } - // console.log('invoices: ',invoices); }); it('should mock GET invoice summary by UUID', async () => { const expected = { diff --git a/tests/mocked/billling.test.ts b/tests/mocked/billling.test.ts index 49f232d53..0730bde85 100644 --- a/tests/mocked/billling.test.ts +++ b/tests/mocked/billling.test.ts @@ -150,80 +150,6 @@ describe('Billing API Resource Tests', () => { expect(balance).toEqual(typeExpected); }); - - // it('should mock GET a list of invoices with pagination', async () => { - // const expected = { - // invoices: [ - // { - // invoice_uuid: '22737513-0ea7-4206-8ceb-98a575af7681', - // amount: '12.34', - // invoice_period: '2019-12', - // }, - // { - // invoice_uuid: 'fdabb512-6faf-443c-ba2e-665452332a9e', - // amount: '23.45', - // invoice_period: '2019-11', - // }, - // ], - // invoice_preview: { - // invoice_uuid: '1afe95e6-0958-4eb0-8d9a-9c5060d3ef03', - // amount: '34.56', - // invoice_period: '2020-02', - // updated_at: '2020-02-23T06:31:50Z', - // }, - // links: { - // pages: { - // next: 'https://api.digitalocean.com/v2/customers/my/invoices?page=2&per_page=20', - // last: 'https://api.digitalocean.com/v2/customers/my/invoices?page=6&per_page=20', - // }, - // }, - // meta: { total: 6 }, - // }; - - // const typeExpected = { - // invoices: [ - // { - // invoiceUuid: '22737513-0ea7-4206-8ceb-98a575af7681', - // amount: '12.34', - // invoicePeriod: '2019-12', - // }, - // { - // invoiceUuid: 'fdabb512-6faf-443c-ba2e-665452332a9e', - // amount: '23.45', - // invoicePeriod: '2019-11', - // }, - // ], - // invoicePreview: { - // invoiceUuid: '1afe95e6-0958-4eb0-8d9a-9c5060d3ef03', - // amount: '34.56', - // invoicePeriod: '2020-02', - // updatedAt: '2020-02-23T06:31:50Z', - // }, - // links: { - // pages: { - // next: 'https://api.digitalocean.com/v2/customers/my/invoices?page=2&per_page=20', - // last: 'https://api.digitalocean.com/v2/customers/my/invoices?page=6&per_page=20', - // }, - // }, - // meta: { total: 6 }, - // }; - - - - // const params = { per_page: 20, page: 1 }; - - // nock(baseUrl) - // .get('/v2/customers/my/invoices') - // .query(params) - // .reply(200, expected); - - // const balance = await client.v2.customers.my.invoices.get({ - // queryParameters: params - // }); - - // expect(balance).toEqual(typeExpected); - // }); - it('should mock GET invoice by UUID', async () => { const expected = { invoice_items: [ @@ -313,7 +239,6 @@ describe('Billing API Resource Tests', () => { .reply(200, expected); const invoices = await client.v2.customers.my.invoices.byInvoice_uuid("1").pdf.get(); - // const listIn = Array.from(invoices); if (invoices) { const decodedInvoice = new TextDecoder('utf-8').decode(invoices) expect(decodedInvoice).toEqual(expected); @@ -321,7 +246,6 @@ describe('Billing API Resource Tests', () => { else { throw new Error('The balance response is undefined'); } - // console.log('invoices: ',invoices); }); it('should mock GET invoice summary by UUID', async () => { diff --git a/tests/mocked/blockstorage.test.js b/tests/mocked/blockstorage.test.js new file mode 100644 index 000000000..18344f5f6 --- /dev/null +++ b/tests/mocked/blockstorage.test.js @@ -0,0 +1,283 @@ +// droplets.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Block Storage API", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should list block storage volumes", async () => { + const expected = { + volumes: [ + { + id: "506f78a4-e098-11e5-ad9f-000f53306ae1", + region: { + name: "New York 1", + slug: "nyc1", + sizes: ["s-1vcpu-1gb", "s-1vcpu-2gb"], + features: ["private_networking", "backups"], + available: true, + }, + droplet_ids: [], + name: "example", + description: "Block store for examples", + size_gigabytes: 10, + created_at: "2016-03-02T17:00:49Z", + filesystem_type: "ext4", + filesystem_label: "example", + tags: ["aninterestingtag"], + }, + ], + links: {}, + meta: { total: 1 }, + }; + const typeExpected = { + volumes: [ + { + id: "506f78a4-e098-11e5-ad9f-000f53306ae1", + region: { + name: "New York 1", + slug: "nyc1", + sizes: ["s-1vcpu-1gb", "s-1vcpu-2gb"], + features: ["private_networking", "backups"], + available: true, + }, + dropletIds: [], + name: "example", + description: "Block store for examples", + sizeGigabytes: 10, + createdAt: "2016-03-02T17:00:49Z", + filesystemType: "ext4", + filesystemLabel: "example", + tags: ["aninterestingtag"], + }, + ], + links: {}, + meta: { total: 1 }, + }; + nock(baseUrl).get("/v2/volumes").reply(200, expected); + const resp = await client.v2.volumes.get(); + expect(resp).toEqual(typeExpected); + }); + it("should create a block storage volume", async () => { + const volumeReq = { + sizeGigabytes: 10, + name: `test-volume`, + description: `Block store for sample`, + region: `nyc3`, + filesystemType: `ext4`, + filesystemLabel: `ext4_volume_01` + }; + const volumeReqNock = { + size_gigabytes: 10, + name: `test-volume`, + description: `Block store for sample`, + region: `nyc3`, + filesystem_type: `ext4`, + filesystem_label: `ext4_volume_01` + }; + const expected = { + volume: { + id: "506f78a4-e098-11e5-ad9f-000f53306ae1", + region: { + name: "New York 1", + slug: "nyc1", + sizes: [ + "s-1vcpu-1gb", + "s-1vcpu-2gb", + "s-1vcpu-3gb", + "s-2vcpu-2gb", + "s-3vcpu-1gb", + "s-2vcpu-4gb", + "s-4vcpu-8gb", + "s-6vcpu-16gb", + "s-8vcpu-32gb", + "s-12vcpu-48gb", + "s-16vcpu-64gb", + "s-20vcpu-96gb", + "s-24vcpu-128gb", + "s-32vcpu-192gb", + ], + features: ["private_networking", "backups", "ipv6", "metadata"], + available: true, + }, + droplet_ids: [], + name: "example", + description: "Block store for examples", + size_gigabytes: 10, + filesystem_type: "ext4", + filesystem_label: "example", + created_at: "2020-03-02T17:00:49Z", + }, + }; + const typeExpected = { + volume: { + id: "506f78a4-e098-11e5-ad9f-000f53306ae1", + region: { + name: "New York 1", + slug: "nyc1", + sizes: [ + "s-1vcpu-1gb", + "s-1vcpu-2gb", + "s-1vcpu-3gb", + "s-2vcpu-2gb", + "s-3vcpu-1gb", + "s-2vcpu-4gb", + "s-4vcpu-8gb", + "s-6vcpu-16gb", + "s-8vcpu-32gb", + "s-12vcpu-48gb", + "s-16vcpu-64gb", + "s-20vcpu-96gb", + "s-24vcpu-128gb", + "s-32vcpu-192gb", + ], + features: ["private_networking", "backups", "ipv6", "metadata"], + available: true, + }, + dropletIds: [], + name: "example", + description: "Block store for examples", + sizeGigabytes: 10, + filesystemType: "ext4", + filesystemLabel: "example", + createdAt: "2020-03-02T17:00:49Z", + }, + }; + nock(baseUrl).post("/v2/volumes", volumeReqNock).reply(201, expected); + const resp = await client.v2.volumes.post(volumeReq); + expect(resp).toEqual(typeExpected); + }); + it("should delete a block storage volume by name", async () => { + const volumeName = "volname"; + const region = "nyc1"; + nock(baseUrl) + .delete(`/v2/volumes?name=${volumeName}®ion=${region}`) + .reply(204); + const resp = await client.v2.volumes.delete({ queryParameters: { name: volumeName, region } }); + expect(resp).toBeUndefined(); + }); + it("should retrieve a block storage snapshot", async () => { + const snapshotId = "506f78a4-e098-11e5-ad9f-000f53306ae1"; + const expected = { + snapshot: { + id: snapshotId, + name: "big-data-snapshot1475261774", + regions: ["nyc1"], + created_at: "2020-09-30T18:56:14Z", + resource_id: "82a48a18-873f-11e6-96bf-000f53315a41", + resource_type: "volume", + min_disk_size: 10, + size_gigabytes: 10, + tags: ["aninterestingtag"], + }, + }; + const typeExpected = { + snapshot: { + id: snapshotId, + name: "big-data-snapshot1475261774", + regions: ["nyc1"], + createdAt: new Date("2020-09-30T18:56:14Z"), + resourceId: "82a48a18-873f-11e6-96bf-000f53315a41", + resourceType: "volume", + minDiskSize: 10, + sizeGigabytes: 10, + tags: ["aninterestingtag"], + }, + }; + nock(baseUrl).get(`/v2/volumes/snapshots/${snapshotId}`).reply(200, expected); + const resp = await client.v2.volumes.snapshots.bySnapshot_id(snapshotId).get(); + expect(resp).toEqual(typeExpected); + }); + it("should list block storage snapshots", async () => { + const volumeId = "1234"; + const expected = { + snapshots: [ + { + id: "6372321", + name: "web-01-1595954862243", + created_at: "2020-07-28T16:47:44Z", + regions: ["nyc3", "sfo3"], + resource_id: "200776916", + resource_type: "droplet", + min_disk_size: 25, + size_gigabytes: 2.34, + tags: ["web", "env:prod"], + }, + ], + links: {}, + meta: { total: 1 }, + }; + const typeExpected = { + snapshots: [ + { + id: "6372321", + name: "web-01-1595954862243", + createdAt: new Date("2020-07-28T16:47:44Z"), + regions: ["nyc3", "sfo3"], + resourceId: "200776916", + resourceType: "droplet", + minDiskSize: 25, + sizeGigabytes: 2.34, + tags: ["web", "env:prod"], + }, + ], + links: {}, + meta: { total: 1 }, + }; + nock(baseUrl).get(`/v2/volumes/${volumeId}/snapshots`).reply(200, expected); + const resp = await client.v2.volumes.byVolume_id(volumeId).snapshots.get(); + expect(resp).toEqual(typeExpected); + }); + it("should delete a block storage snapshot", async () => { + const snapshotId = "6372321"; + nock(baseUrl).delete(`/v2/volumes/snapshots/${snapshotId}`).reply(204); + const resp = await client.v2.volumes.snapshots.bySnapshot_id(snapshotId).delete(); + expect(resp).toBeUndefined(); + }); + it("should delete a block storage volume", async () => { + const volumeId = "6372321"; + nock(baseUrl).delete(`/v2/volumes/${volumeId}`).reply(204); + const resp = await client.v2.volumes.byVolume_id(volumeId).delete(); + expect(resp).toBeUndefined(); + }); + it("should create a block storage snapshot", async () => { + const volumeId = "1234"; + const requestBody = { name: "big-data-snapshot1475261774" }; + const expected = { + snapshot: { + id: "8fa70202-873f-11e6-8b68-000f533176b1", + name: "big-data-snapshot1475261774", + regions: ["nyc1"], + created_at: "2020-09-30T18:56:14Z", + resource_id: "82a48a18-873f-11e6-96bf-000f53315a41", + resource_type: "volume", + min_disk_size: 10, + size_gigabytes: 10, + tags: ["aninterestingtag"], + }, + }; + const typeExpected = { + snapshot: { + id: "8fa70202-873f-11e6-8b68-000f533176b1", + name: "big-data-snapshot1475261774", + regions: ["nyc1"], + createdAt: new Date("2020-09-30T18:56:14Z"), + resourceId: "82a48a18-873f-11e6-96bf-000f53315a41", + resourceType: "volume", + minDiskSize: 10, + sizeGigabytes: 10, + tags: ["aninterestingtag"], + }, + }; + nock(baseUrl).post(`/v2/volumes/${volumeId}/snapshots`, requestBody).reply(201, expected); + const resp = await client.v2.volumes.byVolume_id(volumeId).snapshots.post(requestBody); + expect(resp).toEqual(typeExpected); + }); +}); diff --git a/tests/mocked/blockstorage.test.ts b/tests/mocked/blockstorage.test.ts new file mode 100644 index 000000000..b9b599cd0 --- /dev/null +++ b/tests/mocked/blockstorage.test.ts @@ -0,0 +1,316 @@ +// droplets.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Volumes_ext4 } from "../../src/dots/models/index.js"; + + + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Block Storage API", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should list block storage volumes", async () => { + const expected = { + volumes: [ + { + id: "506f78a4-e098-11e5-ad9f-000f53306ae1", + region: { + name: "New York 1", + slug: "nyc1", + sizes: ["s-1vcpu-1gb", "s-1vcpu-2gb"], + features: ["private_networking", "backups"], + available: true, + }, + droplet_ids: [], + name: "example", + description: "Block store for examples", + size_gigabytes: 10, + created_at: "2016-03-02T17:00:49Z", + filesystem_type: "ext4", + filesystem_label: "example", + tags: ["aninterestingtag"], + }, + ], + links: {}, + meta: { total: 1 }, + }; + + const typeExpected = { + volumes: [ + { + id: "506f78a4-e098-11e5-ad9f-000f53306ae1", + region: { + name: "New York 1", + slug: "nyc1", + sizes: ["s-1vcpu-1gb", "s-1vcpu-2gb"], + features: ["private_networking", "backups"], + available: true, + }, + dropletIds: [], + name: "example", + description: "Block store for examples", + sizeGigabytes: 10, + createdAt: "2016-03-02T17:00:49Z", + filesystemType: "ext4", + filesystemLabel: "example", + tags: ["aninterestingtag"], + }, + ], + links: {}, + meta: { total: 1 }, + }; + + nock(baseUrl).get("/v2/volumes").reply(200, expected); + + const resp = await client.v2.volumes.get(); + expect(resp).toEqual(typeExpected); + }); + + it("should create a block storage volume", async () => { + const volumeReq : Volumes_ext4= { + sizeGigabytes: 10, + name: `test-volume`, + description: `Block store for sample`, + region: `nyc3`, + filesystemType: `ext4`, + filesystemLabel: `ext4_volume_01` + }; + const volumeReqNock= { + size_gigabytes: 10, + name: `test-volume`, + description: `Block store for sample`, + region: `nyc3`, + filesystem_type: `ext4`, + filesystem_label: `ext4_volume_01` + }; + + const expected = { + volume: { + id: "506f78a4-e098-11e5-ad9f-000f53306ae1", + region: { + name: "New York 1", + slug: "nyc1", + sizes: [ + "s-1vcpu-1gb", + "s-1vcpu-2gb", + "s-1vcpu-3gb", + "s-2vcpu-2gb", + "s-3vcpu-1gb", + "s-2vcpu-4gb", + "s-4vcpu-8gb", + "s-6vcpu-16gb", + "s-8vcpu-32gb", + "s-12vcpu-48gb", + "s-16vcpu-64gb", + "s-20vcpu-96gb", + "s-24vcpu-128gb", + "s-32vcpu-192gb", + ], + features: ["private_networking", "backups", "ipv6", "metadata"], + available: true, + }, + droplet_ids: [], + name: "example", + description: "Block store for examples", + size_gigabytes: 10, + filesystem_type: "ext4", + filesystem_label: "example", + created_at: "2020-03-02T17:00:49Z", + }, + }; + + const typeExpected = { + volume: { + id: "506f78a4-e098-11e5-ad9f-000f53306ae1", + region: { + name: "New York 1", + slug: "nyc1", + sizes: [ + "s-1vcpu-1gb", + "s-1vcpu-2gb", + "s-1vcpu-3gb", + "s-2vcpu-2gb", + "s-3vcpu-1gb", + "s-2vcpu-4gb", + "s-4vcpu-8gb", + "s-6vcpu-16gb", + "s-8vcpu-32gb", + "s-12vcpu-48gb", + "s-16vcpu-64gb", + "s-20vcpu-96gb", + "s-24vcpu-128gb", + "s-32vcpu-192gb", + ], + features: ["private_networking", "backups", "ipv6", "metadata"], + available: true, + }, + dropletIds: [], + name: "example", + description: "Block store for examples", + sizeGigabytes: 10, + filesystemType: "ext4", + filesystemLabel: "example", + createdAt: "2020-03-02T17:00:49Z", + }, + }; + nock(baseUrl).post("/v2/volumes",volumeReqNock).reply(201, expected); + const resp = await client.v2.volumes.post(volumeReq); + expect(resp).toEqual(typeExpected); +}); + + it("should delete a block storage volume by name", async () => { + const volumeName = "volname"; + const region = "nyc1"; + + nock(baseUrl) + .delete(`/v2/volumes?name=${volumeName}®ion=${region}`) + .reply(204); + + const resp = await client.v2.volumes.delete({ queryParameters: { name: volumeName, region } }); + expect(resp).toBeUndefined(); + }); + + it("should retrieve a block storage snapshot", async () => { + const snapshotId = "506f78a4-e098-11e5-ad9f-000f53306ae1"; + const expected = { + snapshot: { + id: snapshotId, + name: "big-data-snapshot1475261774", + regions: ["nyc1"], + created_at: "2020-09-30T18:56:14Z", + resource_id: "82a48a18-873f-11e6-96bf-000f53315a41", + resource_type: "volume", + min_disk_size: 10, + size_gigabytes: 10, + tags: ["aninterestingtag"], + }, + }; + + const typeExpected = { + snapshot: { + id: snapshotId, + name: "big-data-snapshot1475261774", + regions: ["nyc1"], + createdAt: new Date("2020-09-30T18:56:14Z"), + resourceId: "82a48a18-873f-11e6-96bf-000f53315a41", + resourceType: "volume", + minDiskSize: 10, + sizeGigabytes: 10, + tags: ["aninterestingtag"], + }, + }; + + nock(baseUrl).get(`/v2/volumes/snapshots/${snapshotId}`).reply(200, expected); + + const resp = await client.v2.volumes.snapshots.bySnapshot_id(snapshotId).get(); + expect(resp).toEqual(typeExpected); + }); + + it("should list block storage snapshots", async () => { + const volumeId = "1234"; + const expected = { + snapshots: [ + { + id: "6372321", + name: "web-01-1595954862243", + created_at: "2020-07-28T16:47:44Z", + regions: ["nyc3", "sfo3"], + resource_id: "200776916", + resource_type: "droplet", + min_disk_size: 25, + size_gigabytes: 2.34, + tags: ["web", "env:prod"], + }, + ], + links: {}, + meta: { total: 1 }, + }; + + const typeExpected = { + snapshots: [ + { + id: "6372321", + name: "web-01-1595954862243", + createdAt: new Date("2020-07-28T16:47:44Z"), + regions: ["nyc3", "sfo3"], + resourceId: "200776916", + resourceType: "droplet", + minDiskSize: 25, + sizeGigabytes: 2.34, + tags: ["web", "env:prod"], + }, + ], + links: {}, + meta: { total: 1 }, + }; + + nock(baseUrl).get(`/v2/volumes/${volumeId}/snapshots`).reply(200, expected); + + const resp = await client.v2.volumes.byVolume_id(volumeId).snapshots.get(); + expect(resp).toEqual(typeExpected); + }); + + it("should delete a block storage snapshot", async () => { + const snapshotId = "6372321"; + + nock(baseUrl).delete(`/v2/volumes/snapshots/${snapshotId}`).reply(204); + + const resp = await client.v2.volumes.snapshots.bySnapshot_id(snapshotId).delete(); + expect(resp).toBeUndefined(); + }); + + it("should delete a block storage volume", async () => { + const volumeId = "6372321"; + + nock(baseUrl).delete(`/v2/volumes/${volumeId}`).reply(204); + + const resp = await client.v2.volumes.byVolume_id(volumeId).delete(); + expect(resp).toBeUndefined(); + }); + + it("should create a block storage snapshot", async () => { + const volumeId = "1234"; + const requestBody = { name: "big-data-snapshot1475261774" }; + const expected = { + snapshot: { + id: "8fa70202-873f-11e6-8b68-000f533176b1", + name: "big-data-snapshot1475261774", + regions: ["nyc1"], + created_at: "2020-09-30T18:56:14Z", + resource_id: "82a48a18-873f-11e6-96bf-000f53315a41", + resource_type: "volume", + min_disk_size: 10, + size_gigabytes: 10, + tags: ["aninterestingtag"], + }, + }; + + const typeExpected = { + snapshot: { + id: "8fa70202-873f-11e6-8b68-000f533176b1", + name: "big-data-snapshot1475261774", + regions: ["nyc1"], + createdAt: new Date("2020-09-30T18:56:14Z"), + resourceId: "82a48a18-873f-11e6-96bf-000f53315a41", + resourceType: "volume", + minDiskSize: 10, + sizeGigabytes: 10, + tags: ["aninterestingtag"], + }, + }; + + nock(baseUrl).post(`/v2/volumes/${volumeId}/snapshots`, requestBody).reply(201, expected); + + const resp = await client.v2.volumes.byVolume_id(volumeId).snapshots.post(requestBody); + expect(resp).toEqual(typeExpected); + }); +}); \ No newline at end of file diff --git a/tests/mocked/cdn.test.js b/tests/mocked/cdn.test.js new file mode 100644 index 000000000..f356c6139 --- /dev/null +++ b/tests/mocked/cdn.test.js @@ -0,0 +1,150 @@ +// cdn.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("CDN API", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should create a CDN endpoint", async () => { + const createReq = { + origin: "static-images.nyc3.digitaloceanspaces.com", + ttl: 3600 + }; + const createReqNock = { + origin: "static-images.nyc3.digitaloceanspaces.com", + ttl: 3600 + }; + const expected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + created_at: "2018-07-19T15:04:16Z", + ttl: 3600, + } + }; + const typeExpected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + createdAt: new Date("2018-07-19T15:04:16Z"), + ttl: 3600, + } + }; + nock(baseUrl).post("/v2/cdn/endpoints", createReqNock).reply(201, expected); + const resp = await client.v2.cdn.endpoints.post(createReq); + expect(resp).toEqual(typeExpected); + }); + it("should list CDN endpoints", async () => { + const expected = { + endpoints: [ + { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + created_at: "2018-07-19T15:04:16Z", + certificate_id: "892071a0-bb95-49bc-8021-3afd67a210bf", + custom_domain: "static.example.com", + ttl: 3600, + } + ], + links: {}, + meta: { total: 1 } + }; + const typeExpected = { + endpoints: [ + { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + createdAt: new Date("2018-07-19T15:04:16Z"), + certificateId: "892071a0-bb95-49bc-8021-3afd67a210bf", + customDomain: "static.example.com", + ttl: 3600, + } + ], + links: {}, + meta: { total: 1 } + }; + nock(baseUrl).get("/v2/cdn/endpoints").reply(200, expected); + const resp = await client.v2.cdn.endpoints.get(); + expect(resp).toEqual(typeExpected); + }); + it("should get a CDN endpoint", async () => { + const endpointId = "aa34ba1"; + const expected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + created_at: "2018-07-19T15:04:16Z", + ttl: 3600, + } + }; + const typeExpected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + createdAt: new Date("2018-07-19T15:04:16Z"), + ttl: 3600, + } + }; + nock(baseUrl).get(`/v2/cdn/endpoints/${endpointId}`).reply(200, expected); + const resp = await client.v2.cdn.endpoints.byCdn_id(endpointId).get(); + expect(resp).toEqual(typeExpected); + }); + it("should update a CDN endpoint", async () => { + const endpointId = "aa34ba1"; + const updateReq = { + ttl: 3600, + }; + const expected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + created_at: "2018-07-19T15:04:16Z", + ttl: 3600, + } + }; + const typeExpected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + createdAt: new Date("2018-07-19T15:04:16Z"), + ttl: 3600, + } + }; + nock(baseUrl).put(`/v2/cdn/endpoints/${endpointId}`, updateReq).reply(200, expected); + const resp = await client.v2.cdn.endpoints.byCdn_id(endpointId).put(updateReq); + expect(resp).toEqual(typeExpected); + }); + it("should delete a CDN endpoint", async () => { + const endpointId = "1"; + nock(baseUrl).delete(`/v2/cdn/endpoints/${endpointId}`).reply(204); + const resp = await client.v2.cdn.endpoints.byCdn_id(endpointId).delete(); + expect(resp).toBeUndefined(); + }); + it("should purge CDN cache", async () => { + const endpointId = "1"; + const purgeReq = { + files: ["path/to/image.png", "path/to/css/*"] + }; + const purgeReqNock = { + files: ["path/to/image.png", "path/to/css/*"] + }; + nock(baseUrl).delete(`/v2/cdn/endpoints/${endpointId}/cache`, purgeReqNock).reply(204); + const resp = await client.v2.cdn.endpoints.byCdn_id(endpointId).cache.delete(purgeReq); + expect(resp).toBeUndefined(); + }); +}); diff --git a/tests/mocked/cdn.test.ts b/tests/mocked/cdn.test.ts new file mode 100644 index 000000000..e15f6c7d1 --- /dev/null +++ b/tests/mocked/cdn.test.ts @@ -0,0 +1,178 @@ +// cdn.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Cdn_endpoint } from "../../src/dots/models/index.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("CDN API", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should create a CDN endpoint", async () => { + const createReq: Cdn_endpoint = { + origin: "static-images.nyc3.digitaloceanspaces.com", + ttl: 3600 + }; + + const createReqNock = { + origin: "static-images.nyc3.digitaloceanspaces.com", + ttl: 3600 + }; + + const expected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + created_at: "2018-07-19T15:04:16Z", + ttl: 3600, + } + }; + + const typeExpected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + createdAt: new Date("2018-07-19T15:04:16Z"), + ttl: 3600, + } + }; + + nock(baseUrl).post("/v2/cdn/endpoints", createReqNock).reply(201, expected); + + const resp = await client.v2.cdn.endpoints.post(createReq); + expect(resp).toEqual(typeExpected); + }); + + it("should list CDN endpoints", async () => { + const expected = { + endpoints: [ + { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + created_at: "2018-07-19T15:04:16Z", + certificate_id: "892071a0-bb95-49bc-8021-3afd67a210bf", + custom_domain: "static.example.com", + ttl: 3600, + } + ], + links: {}, + meta: { total: 1 } + }; + + const typeExpected = { + endpoints: [ + { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + createdAt: new Date("2018-07-19T15:04:16Z"), + certificateId: "892071a0-bb95-49bc-8021-3afd67a210bf", + customDomain: "static.example.com", + ttl: 3600, + } + ], + links: {}, + meta: { total: 1 } + }; + + nock(baseUrl).get("/v2/cdn/endpoints").reply(200, expected); + + const resp = await client.v2.cdn.endpoints.get(); + expect(resp).toEqual(typeExpected); + }); + + it("should get a CDN endpoint", async () => { + const endpointId = "aa34ba1"; + const expected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + created_at: "2018-07-19T15:04:16Z", + ttl: 3600, + } + }; + + const typeExpected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + createdAt: new Date("2018-07-19T15:04:16Z"), + ttl: 3600, + } + }; + + nock(baseUrl).get(`/v2/cdn/endpoints/${endpointId}`).reply(200, expected); + + const resp = await client.v2.cdn.endpoints.byCdn_id(endpointId).get(); + expect(resp).toEqual(typeExpected); + }); + + it("should update a CDN endpoint", async () => { + const endpointId = "aa34ba1"; + const updateReq={ + ttl: 3600, + }; + const expected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + created_at: "2018-07-19T15:04:16Z", + ttl: 3600, + } + }; + + const typeExpected = { + endpoint: { + id: "19f06b6a-3ace-4315-b086-499a0e521b76", + origin: "static-images.nyc3.digitaloceanspaces.com", + endpoint: "static-images.nyc3.cdn.digitaloceanspaces.com", + createdAt: new Date("2018-07-19T15:04:16Z"), + ttl: 3600, + } + }; + + nock(baseUrl).put(`/v2/cdn/endpoints/${endpointId}`, updateReq).reply(200, expected); + + const resp = await client.v2.cdn.endpoints.byCdn_id(endpointId).put(updateReq); + expect(resp).toEqual(typeExpected); + }); + + it("should delete a CDN endpoint", async () => { + const endpointId = "1"; + + nock(baseUrl).delete(`/v2/cdn/endpoints/${endpointId}`).reply(204); + + const resp = await client.v2.cdn.endpoints.byCdn_id(endpointId).delete(); + expect(resp).toBeUndefined(); + }); + + it("should purge CDN cache", async () => { + const endpointId = "1"; + const purgeReq = { + files: ["path/to/image.png", "path/to/css/*"] + }; + + const purgeReqNock = { + files: ["path/to/image.png", "path/to/css/*"] + }; + + nock(baseUrl).delete(`/v2/cdn/endpoints/${endpointId}/cache`, purgeReqNock).reply(204); + + const resp = await client.v2.cdn.endpoints.byCdn_id(endpointId).cache.delete(purgeReq); + expect(resp).toBeUndefined(); + }); +}); \ No newline at end of file diff --git a/tests/mocked/containerRegistry.test.js b/tests/mocked/containerRegistry.test.js new file mode 100644 index 000000000..adf5d5112 --- /dev/null +++ b/tests/mocked/containerRegistry.test.js @@ -0,0 +1,489 @@ +// container-registry.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Container Registry API", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should create a container registry", async () => { + const createReq = { + name: "example", + subscriptionTierSlug: "basic", + region: "fra1" + }; + const createReqNock = { + name: "example", + subscription_tier_slug: "basic", + region: "fra1" + }; + const expected = { + registry: { + name: "example", + created_at: "2020-03-21T16:02:37Z", + region: "fra1", + storage_usage_bytes: 29393920, + storage_usage_bytes_updated_at: "2020-11-04T21:39:49.530562231Z", + subscription: { + tier: { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + storage_overage_price_in_cents: 2, + }, + created_at: "2020-01-23T21:19:12Z", + updated_at: "2020-11-05T15:53:24Z", + }, + } + }; + const typeExpected = { + registry: { + name: "example", + createdAt: new Date("2020-03-21T16:02:37Z"), + region: "fra1", + storageUsageBytes: 29393920, + storageUsageBytesUpdatedAt: new Date("2020-11-04T21:39:49.530562231Z"), + subscription: { + tier: { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + storageOveragePriceInCents: 2, + }, + createdAt: new Date("2020-01-23T21:19:12Z"), + updatedAt: new Date("2020-11-05T15:53:24Z"), + }, + } + }; + nock(baseUrl).post("/v2/registry", createReqNock).reply(201, expected); + const resp = await client.v2.registry.post(createReq); + expect(resp).toEqual(typeExpected); + }); + it("should get a container registry", async () => { + const expected = { + registry: { + name: "example", + created_at: "2020-03-21T16:02:37Z", + region: "fra1", + storage_usage_bytes: 29393920, + storage_usage_bytes_updated_at: "2020-11-04T21:39:49.530562231Z", + subscription: { + tier: { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + storage_overage_price_in_cents: 2, + }, + created_at: "2020-01-23T21:19:12Z", + updated_at: "2020-11-05T15:53:24Z", + }, + } + }; + const typeExpected = { + registry: { + name: "example", + createdAt: new Date("2020-03-21T16:02:37Z"), + region: "fra1", + storageUsageBytes: 29393920, + storageUsageBytesUpdatedAt: new Date("2020-11-04T21:39:49.530562231Z"), + subscription: { + tier: { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + storageOveragePriceInCents: 2, + }, + createdAt: new Date("2020-01-23T21:19:12Z"), + updatedAt: new Date("2020-11-05T15:53:24Z"), + }, + } + }; + nock(baseUrl).get("/v2/registry").reply(200, expected); + const resp = await client.v2.registry.get(); + expect(resp).toEqual(typeExpected); + }); + it("should delete a container registry", async () => { + nock(baseUrl).delete("/v2/registry").reply(204); + const resp = await client.v2.registry.delete(); + expect(resp).toBeUndefined(); + }); + it("should get subscription information", async () => { + const expected = { + subscription: { + tier: { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + storage_overage_price_in_cents: 2, + }, + created_at: "2020-01-23T21:19:12Z", + updated_at: "2020-11-05T15:53:24Z", + } + }; + const typeExpected = { + subscription: { + tier: { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + storageOveragePriceInCents: 2, + }, + createdAt: new Date("2020-01-23T21:19:12Z"), + updatedAt: new Date("2020-11-05T15:53:24Z"), + } + }; + nock(baseUrl).get("/v2/registry/subscription").reply(200, expected); + const resp = await client.v2.registry.subscription.get(); + expect(resp).toEqual(typeExpected); + }); + it("should update subscription", async () => { + const updateReq = { tierSlug: "basic" }; + const updateReqNock = { tier_slug: "basic" }; + const expected = { + subscription: { + tier: { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + storage_overage_price_in_cents: 2, + }, + created_at: "2020-01-23T21:19:12Z", + updated_at: "2020-11-05T15:53:24Z", + } + }; + const typeExpected = { + subscription: { + tier: { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + storageOveragePriceInCents: 2, + }, + createdAt: new Date("2020-01-23T21:19:12Z"), + updatedAt: new Date("2020-11-05T15:53:24Z"), + } + }; + nock(baseUrl).post("/v2/registry/subscription", updateReqNock).reply(200, expected); + const resp = await client.v2.registry.subscription.post(updateReq); + expect(resp).toEqual(typeExpected); + }); + it("should get docker credentials", async () => { + const expected = { + auths: { + "registry.digitalocean.com": { auth: "YjdkMDNhNjk0N2IyMZDM1MDQ1ODIK" } + } + }; + const typeExpected = { + auths: { + "registryDigitaloceanCom": { auth: "YjdkMDNhNjk0N2IyMZDM1MDQ1ODIK" } + } + }; + nock(baseUrl).get("/v2/registry/docker-credentials").reply(200, expected); + const resp = await client.v2.registry.dockerCredentials.get(); + expect(resp).toEqual(typeExpected); + }); + it("should validate registry name", async () => { + const validateReq = { name: "example" }; + nock(baseUrl).post("/v2/registry/validate-name", validateReq).reply(204); + const resp = await client.v2.registry.validateName.post(validateReq); + expect(resp).toBeUndefined(); + }); + it("should list repositories", async () => { + const registryName = "example"; + const expected = { + repositories: [ + { + registry_name: "example", + name: "repo-1", + tag_count: 57, + manifest_count: 82, + latest_manifest: { + digest: "sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221", + registry_name: "example", + repository: "repo-1", + compressed_size_bytes: 1972332, + size_bytes: 2816445, + updated_at: "2021-04-09T23:54:25Z", + tags: ["v1", "v2"], + blobs: [ + { + digest: "sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab", + compressed_size_bytes: 1471, + }, + { + digest: "sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e", + }, + { + digest: "sha256:69704ef328d05a9f806b6b8502915e6a0a4faa4d72018dc42343f511490daf8a", + compressed_size_bytes: 528, + }, + ], + }, + } + ], + meta: { total: 5 }, + links: { + pages: { + next: "https://api.digitalocean.com/v2/registry/example/repositoriesV2?page=2&page_token=JPZmZzZXQiOjB9&per_page=1", + last: "https://api.digitalocean.com/v2/registry/example/repositoriesV2?page=5&per_page=1", + } + }, + }; + const typeExpected = { + repositories: [ + { + registryName: "example", + name: "repo-1", + tagCount: 57, + manifestCount: 82, + latestManifest: { + digest: "sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221", + registryName: "example", + repository: "repo-1", + compressedSizeBytes: 1972332, + sizeBytes: 2816445, + updatedAt: new Date("2021-04-09T23:54:25Z"), + tags: ["v1", "v2"], + blobs: [ + { + digest: "sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab", + compressedSizeBytes: 1471, + }, + { + digest: "sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e", + }, + { + digest: "sha256:69704ef328d05a9f806b6b8502915e6a0a4faa4d72018dc42343f511490daf8a", + compressedSizeBytes: 528, + }, + ], + }, + } + ], + meta: { total: 5 }, + links: { + pages: { + additionalData: { + next: "https://api.digitalocean.com/v2/registry/example/repositoriesV2?page=2&page_token=JPZmZzZXQiOjB9&per_page=1", + last: "https://api.digitalocean.com/v2/registry/example/repositoriesV2?page=5&per_page=1", + } + } + }, + }; + nock(baseUrl).get(`/v2/registry/${registryName}/repositoriesV2`).reply(200, expected); + const resp = await client.v2.registry.byRegistry_name(registryName).repositoriesV2.get(); + expect(resp).toEqual(typeExpected); + }); + it("should list repository tags", async () => { + const registryName = "example"; + const repositoryName = "repo-1"; + const expected = { + tags: [ + { + registry_name: "example", + repository: "repo-1", + tag: "latest", + manifest_digest: "sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221", + compressed_size_bytes: 2803255, + size_bytes: 5861888, + updated_at: "2020-04-09T23:54:25Z", + } + ], + meta: { total: 1 }, + }; + const typeExpected = { + tags: [ + { + registryName: "example", + repository: "repo-1", + tag: "latest", + manifestDigest: "sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221", + compressedSizeBytes: 2803255, + sizeBytes: 5861888, + updatedAt: new Date("2020-04-09T23:54:25Z"), + } + ], + meta: { total: 1 }, + }; + nock(baseUrl).get(`/v2/registry/${registryName}/repositories/${repositoryName}/tags`).reply(200, expected); + const resp = await client.v2.registry.byRegistry_name(registryName).repositories.byRepository_name(repositoryName).tags.get(); + expect(resp).toEqual(typeExpected); + }); + it("should delete repository tag", async () => { + const registryName = "example"; + const repositoryName = "repo-1"; + const repositoryTag = "tag1"; + nock(baseUrl).delete(`/v2/registry/${registryName}/repositories/${repositoryName}/tags/${repositoryTag}`).reply(204); + const resp = await client.v2.registry.byRegistry_name(registryName).repositories.byRepository_name(repositoryName).tags.byRepository_tag(repositoryTag).delete(); + expect(resp).toBeUndefined(); + }); + it("should start garbage collection", async () => { + const registryName = "example"; + const payLoadNock = { + type: "unreferenced blobs only", + }; + const expected = { + garbage_collection: { + uuid: "eff0feee-49c7-4e8f-ba5c-a320c109c8a8", + registry_name: "example", + status: "requested", + created_at: "2020-10-30T21:03:24Z", + updated_at: "2020-10-30T21:03:44Z", + blobs_deleted: 42, + freed_bytes: 667, + }, + }; + const typeExpected = { + garbageCollection: { + uuid: "eff0feee-49c7-4e8f-ba5c-a320c109c8a8", + registryName: "example", + status: "requested", + createdAt: new Date("2020-10-30T21:03:24Z"), + updatedAt: new Date("2020-10-30T21:03:44Z"), + blobsDeleted: 42, + freedBytes: 667, + }, + }; + nock(baseUrl).post(`/v2/registry/${registryName}/garbage-collection`, payLoadNock).reply(201, expected); + const payLoad = { + type: "unreferenced blobs only", + }; + const resp = await client.v2.registry.byRegistry_name(registryName).garbageCollection.post(payLoad); + expect(resp).toStrictEqual(typeExpected); + }); + it("should list garbage collections", async () => { + const registryName = "example"; + const expected = { + garbage_collections: [ + { + uuid: "eff0feee-49c7-4e8f-ba5c-a320c109c8a8", + registry_name: "example", + status: "requested", + created_at: "2020-10-30T21:03:24.000Z", + updated_at: "2020-10-30T21:03:44.000Z", + blobs_deleted: 42, + freed_bytes: 667, + } + ], + meta: { total: 1 }, + }; + const typeExpected = { + garbageCollections: [ + { + uuid: "eff0feee-49c7-4e8f-ba5c-a320c109c8a8", + registryName: "example", + status: "requested", + createdAt: new Date("2020-10-30T21:03:24.000Z"), + updatedAt: new Date("2020-10-30T21:03:44.000Z"), + blobsDeleted: 42, + freedBytes: 667, + } + ], + additionalData: { + meta: { total: 1 }, + } + }; + nock(baseUrl).get(`/v2/registry/${registryName}/garbage-collections`).reply(200, expected); + const resp = await client.v2.registry.byRegistry_name(registryName).garbageCollections.get(); + expect(resp).toEqual(typeExpected); + }); + it("should get registry options", async () => { + const expected = { + options: { + available_regions: ["nyc3", "sfo3", "ams3", "sgp1", "fra1"], + subscription_tiers: [ + { + name: "Starter", + slug: "starter", + included_repositories: 1, + included_storage_bytes: 524288000, + allow_storage_overage: false, + included_bandwidth_bytes: 524288000, + monthly_price_in_cents: 0, + eligible: false, + eligibility_reasons: ["OverRepositoryLimit"], + }, + { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + eligible: true, + }, + ], + } + }; + const typeExpected = { + options: { + availableRegions: ["nyc3", "sfo3", "ams3", "sgp1", "fra1"], + subscriptionTiers: [ + { + name: "Starter", + slug: "starter", + includedRepositories: 1, + includedStorageBytes: 524288000, + allowStorageOverage: false, + includedBandwidthBytes: 524288000, + monthlyPriceInCents: 0, + eligible: false, + eligibilityReasons: ["OverRepositoryLimit"], + }, + { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + eligible: true, + }, + ], + } + }; + nock(baseUrl).get("/v2/registry/options").reply(200, expected); + const resp = await client.v2.registry.optionsPath.get(); + expect(resp).toEqual(typeExpected); + }); +}); diff --git a/tests/mocked/containerRegistry.test.ts b/tests/mocked/containerRegistry.test.ts new file mode 100644 index 000000000..ddab211ff --- /dev/null +++ b/tests/mocked/containerRegistry.test.ts @@ -0,0 +1,541 @@ +// container-registry.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Registry_create } from "../../src/dots/models/index.js"; +import { Registry_run_gc } from "../../src/dots/models/index.js"; +import { SubscriptionPostRequestBody } from "../../src/dots/v2/registry/subscription/index.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Container Registry API", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should create a container registry", async () => { + const createReq: Registry_create = { + name: "example", + subscriptionTierSlug: "basic", + region: "fra1" + }; + + const createReqNock = { + name: "example", + subscription_tier_slug: "basic", + region: "fra1" + }; + + const expected = { + registry: { + name: "example", + created_at: "2020-03-21T16:02:37Z", + region: "fra1", + storage_usage_bytes: 29393920, + storage_usage_bytes_updated_at: "2020-11-04T21:39:49.530562231Z", + subscription: { + tier: { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + storage_overage_price_in_cents: 2, + }, + created_at: "2020-01-23T21:19:12Z", + updated_at: "2020-11-05T15:53:24Z", + }, + } + }; + + const typeExpected = { + registry: { + name: "example", + createdAt: new Date("2020-03-21T16:02:37Z"), + region: "fra1", + storageUsageBytes: 29393920, + storageUsageBytesUpdatedAt: new Date("2020-11-04T21:39:49.530562231Z"), + subscription: { + tier: { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + storageOveragePriceInCents: 2, + }, + createdAt: new Date("2020-01-23T21:19:12Z"), + updatedAt: new Date("2020-11-05T15:53:24Z"), + }, + } + }; + + nock(baseUrl).post("/v2/registry", createReqNock).reply(201, expected); + + const resp = await client.v2.registry.post(createReq); + expect(resp).toEqual(typeExpected); + }); + + it("should get a container registry", async () => { + const expected = { + registry: { + name: "example", + created_at: "2020-03-21T16:02:37Z", + region: "fra1", + storage_usage_bytes: 29393920, + storage_usage_bytes_updated_at: "2020-11-04T21:39:49.530562231Z", + subscription: { + tier: { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + storage_overage_price_in_cents: 2, + }, + created_at: "2020-01-23T21:19:12Z", + updated_at: "2020-11-05T15:53:24Z", + }, + } + }; + + const typeExpected = { + registry: { + name: "example", + createdAt: new Date("2020-03-21T16:02:37Z"), + region: "fra1", + storageUsageBytes: 29393920, + storageUsageBytesUpdatedAt: new Date("2020-11-04T21:39:49.530562231Z"), + subscription: { + tier: { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + storageOveragePriceInCents: 2, + }, + createdAt: new Date("2020-01-23T21:19:12Z"), + updatedAt: new Date("2020-11-05T15:53:24Z"), + }, + } + }; + + nock(baseUrl).get("/v2/registry").reply(200, expected); + + const resp = await client.v2.registry.get(); + expect(resp).toEqual(typeExpected); + }); + + it("should delete a container registry", async () => { + nock(baseUrl).delete("/v2/registry").reply(204); + + const resp = await client.v2.registry.delete(); + expect(resp).toBeUndefined(); + }); + + it("should get subscription information", async () => { + const expected = { + subscription: { + tier: { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + storage_overage_price_in_cents: 2, + }, + created_at: "2020-01-23T21:19:12Z", + updated_at: "2020-11-05T15:53:24Z", + } + }; + + const typeExpected = { + subscription: { + tier: { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + storageOveragePriceInCents: 2, + }, + createdAt: new Date("2020-01-23T21:19:12Z"), + updatedAt: new Date("2020-11-05T15:53:24Z"), + } + }; + + nock(baseUrl).get("/v2/registry/subscription").reply(200, expected); + + const resp = await client.v2.registry.subscription.get(); + expect(resp).toEqual(typeExpected); + }); + + it("should update subscription", async () => { + const updateReq :SubscriptionPostRequestBody = { tierSlug: "basic" }; + const updateReqNock = { tier_slug: "basic" }; + + const expected = { + subscription: { + tier: { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + storage_overage_price_in_cents: 2, + }, + created_at: "2020-01-23T21:19:12Z", + updated_at: "2020-11-05T15:53:24Z", + } + }; + + const typeExpected = { + subscription: { + tier: { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + storageOveragePriceInCents: 2, + }, + createdAt: new Date("2020-01-23T21:19:12Z"), + updatedAt: new Date("2020-11-05T15:53:24Z"), + } + }; + + nock(baseUrl).post("/v2/registry/subscription", updateReqNock).reply(200, expected); + + const resp = await client.v2.registry.subscription.post(updateReq); + expect(resp).toEqual(typeExpected); + }); + + it("should get docker credentials", async () => { + const expected = { + auths: { + "registry.digitalocean.com": { auth: "YjdkMDNhNjk0N2IyMZDM1MDQ1ODIK" } + } + }; + + const typeExpected = { + auths: { + "registryDigitaloceanCom": { auth: "YjdkMDNhNjk0N2IyMZDM1MDQ1ODIK" } + } + }; + + nock(baseUrl).get("/v2/registry/docker-credentials").reply(200, expected); + + const resp = await client.v2.registry.dockerCredentials.get(); + expect(resp).toEqual(typeExpected); + }); + + it("should validate registry name", async () => { + const validateReq = { name: "example" }; + nock(baseUrl).post("/v2/registry/validate-name", validateReq).reply(204); + const resp = await client.v2.registry.validateName.post(validateReq); + expect(resp).toBeUndefined(); + }); + + it("should list repositories", async () => { + const registryName = "example"; + const expected = { + repositories: [ + { + registry_name: "example", + name: "repo-1", + tag_count: 57, + manifest_count: 82, + latest_manifest: { + digest: "sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221", + registry_name: "example", + repository: "repo-1", + compressed_size_bytes: 1972332, + size_bytes: 2816445, + updated_at: "2021-04-09T23:54:25Z", + tags: ["v1", "v2"], + blobs: [ + { + digest: "sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab", + compressed_size_bytes: 1471, + }, + { + digest: "sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e", + }, + { + digest: "sha256:69704ef328d05a9f806b6b8502915e6a0a4faa4d72018dc42343f511490daf8a", + compressed_size_bytes: 528, + }, + ], + }, + } + ], + meta: { total: 5 }, + links: { + pages: { + next: "https://api.digitalocean.com/v2/registry/example/repositoriesV2?page=2&page_token=JPZmZzZXQiOjB9&per_page=1", + last: "https://api.digitalocean.com/v2/registry/example/repositoriesV2?page=5&per_page=1", + } + }, + }; + + const typeExpected = { + repositories: [ + { + registryName: "example", + name: "repo-1", + tagCount: 57, + manifestCount: 82, + latestManifest: { + digest: "sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221", + registryName: "example", + repository: "repo-1", + compressedSizeBytes: 1972332, + sizeBytes: 2816445, + updatedAt: new Date("2021-04-09T23:54:25Z"), + tags: ["v1", "v2"], + blobs: [ + { + digest: "sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab", + compressedSizeBytes: 1471, + }, + { + digest: "sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e", + }, + { + digest: "sha256:69704ef328d05a9f806b6b8502915e6a0a4faa4d72018dc42343f511490daf8a", + compressedSizeBytes: 528, + }, + ], + }, + } + ], + meta: { total: 5 }, + links: { + pages: { + additionalData: { + next: "https://api.digitalocean.com/v2/registry/example/repositoriesV2?page=2&page_token=JPZmZzZXQiOjB9&per_page=1", + last: "https://api.digitalocean.com/v2/registry/example/repositoriesV2?page=5&per_page=1", + } + } + }, + }; + + nock(baseUrl).get(`/v2/registry/${registryName}/repositoriesV2`).reply(200, expected); + + const resp = await client.v2.registry.byRegistry_name(registryName).repositoriesV2.get(); + expect(resp).toEqual(typeExpected); + }); + + it("should list repository tags", async () => { + const registryName = "example"; + const repositoryName = "repo-1"; + const expected = { + tags: [ + { + registry_name: "example", + repository: "repo-1", + tag: "latest", + manifest_digest: "sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221", + compressed_size_bytes: 2803255, + size_bytes: 5861888, + updated_at: "2020-04-09T23:54:25Z", + } + ], + meta: { total: 1 }, + }; + + const typeExpected = { + tags: [ + { + registryName: "example", + repository: "repo-1", + tag: "latest", + manifestDigest: "sha256:cb8a924afdf0229ef7515d9e5b3024e23b3eb03ddbba287f4a19c6ac90b8d221", + compressedSizeBytes: 2803255, + sizeBytes: 5861888, + updatedAt: new Date("2020-04-09T23:54:25Z"), + } + ], + meta: { total: 1 }, + }; + + nock(baseUrl).get(`/v2/registry/${registryName}/repositories/${repositoryName}/tags`).reply(200, expected); + + const resp = await client.v2.registry.byRegistry_name(registryName).repositories.byRepository_name(repositoryName).tags.get(); + expect(resp).toEqual(typeExpected); + }); + + it("should delete repository tag", async () => { + const registryName = "example"; + const repositoryName = "repo-1"; + const repositoryTag = "tag1"; + + nock(baseUrl).delete(`/v2/registry/${registryName}/repositories/${repositoryName}/tags/${repositoryTag}`).reply(204); + + const resp = await client.v2.registry.byRegistry_name(registryName).repositories.byRepository_name(repositoryName).tags.byRepository_tag(repositoryTag).delete(); + expect(resp).toBeUndefined(); + }); + + it("should start garbage collection", async () => { + const registryName = "example"; + const payLoadNock = { + type: "unreferenced blobs only", + }; + const expected = { + garbage_collection: { + uuid: "eff0feee-49c7-4e8f-ba5c-a320c109c8a8", + registry_name: "example", + status: "requested", + created_at: "2020-10-30T21:03:24Z", + updated_at: "2020-10-30T21:03:44Z", + blobs_deleted: 42, + freed_bytes: 667, + }, + }; + + const typeExpected = { + garbageCollection: { + uuid: "eff0feee-49c7-4e8f-ba5c-a320c109c8a8", + registryName: "example", + status: "requested", + createdAt: new Date("2020-10-30T21:03:24Z"), + updatedAt: new Date("2020-10-30T21:03:44Z"), + blobsDeleted: 42, + freedBytes: 667, + }, + }; + nock(baseUrl).post(`/v2/registry/${registryName}/garbage-collection`, payLoadNock).reply(201, expected); + const payLoad : Registry_run_gc = { + type: "unreferenced blobs only", + }; + const resp = await client.v2.registry.byRegistry_name(registryName).garbageCollection.post(payLoad); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should list garbage collections", async () => { + const registryName = "example"; + const expected = { + garbage_collections: [ + { + uuid: "eff0feee-49c7-4e8f-ba5c-a320c109c8a8", + registry_name: "example", + status: "requested", + created_at: "2020-10-30T21:03:24.000Z", + updated_at: "2020-10-30T21:03:44.000Z", + blobs_deleted: 42, + freed_bytes: 667, + } + ], + meta: { total: 1 }, + }; + + const typeExpected = { + garbageCollections: [ + { + uuid: "eff0feee-49c7-4e8f-ba5c-a320c109c8a8", + registryName: "example", + status: "requested", + createdAt: new Date("2020-10-30T21:03:24.000Z"), + updatedAt: new Date("2020-10-30T21:03:44.000Z"), + blobsDeleted: 42, + freedBytes: 667, + } + ], + additionalData: { + meta: { total: 1 }, + } + }; + + nock(baseUrl).get(`/v2/registry/${registryName}/garbage-collections`).reply(200, expected); + + const resp = await client.v2.registry.byRegistry_name(registryName).garbageCollections.get(); + expect(resp).toEqual(typeExpected); + }); + + it("should get registry options", async () => { + const expected = { + options: { + available_regions: ["nyc3", "sfo3", "ams3", "sgp1", "fra1"], + subscription_tiers: [ + { + name: "Starter", + slug: "starter", + included_repositories: 1, + included_storage_bytes: 524288000, + allow_storage_overage: false, + included_bandwidth_bytes: 524288000, + monthly_price_in_cents: 0, + eligible: false, + eligibility_reasons: ["OverRepositoryLimit"], + }, + { + name: "Basic", + slug: "basic", + included_repositories: 5, + included_storage_bytes: 5368709120, + allow_storage_overage: true, + included_bandwidth_bytes: 5368709120, + monthly_price_in_cents: 500, + eligible: true, + }, + ], + } + }; + + const typeExpected = { + options: { + availableRegions: ["nyc3", "sfo3", "ams3", "sgp1", "fra1"], + subscriptionTiers: [ + { + name: "Starter", + slug: "starter", + includedRepositories: 1, + includedStorageBytes: 524288000, + allowStorageOverage: false, + includedBandwidthBytes: 524288000, + monthlyPriceInCents: 0, + eligible: false, + eligibilityReasons: ["OverRepositoryLimit"], + }, + { + name: "Basic", + slug: "basic", + includedRepositories: 5, + includedStorageBytes: 5368709120, + allowStorageOverage: true, + includedBandwidthBytes: 5368709120, + monthlyPriceInCents: 500, + eligible: true, + }, + ], + } + }; + + nock(baseUrl).get("/v2/registry/options").reply(200, expected); + + const resp = await client.v2.registry.optionsPath.get(); + expect(resp).toEqual(typeExpected); + }); +}); \ No newline at end of file diff --git a/tests/mocked/databases.test.js b/tests/mocked/databases.test.js new file mode 100644 index 000000000..bfa8e724c --- /dev/null +++ b/tests/mocked/databases.test.js @@ -0,0 +1,403 @@ +// databases.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Databases API", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should add database", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const addReq = { name: "alpha" }; + const addReqNock = { name: "alpha" }; + const expected = { db: { name: "alpha" } }; + const typeExpected = { db: { name: "alpha" } }; + nock(baseUrl).post(`/v2/databases/${clusterUuid}/dbs`, addReqNock).reply(201, expected); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).dbs.post(addReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should add connection pool", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const addPoolReq = { + name: "backend-pool", + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin", + }; + const addPoolReqNock = { + name: "backend-pool", + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin", + }; + const expected = { + pool: { + user: "doadmin", + name: "backend-pool", + size: 10, + db: "defaultdb", + mode: "transaction", + connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@backend-do-user-19081923-0.db.ondigitalocean.com:25061/backend-pool?sslmode=require", + database: "backend-pool", + host: "backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25061, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + } + }; + nock(baseUrl).post(`/v2/databases/${clusterUuid}/pools`, addPoolReqNock).reply(201, expected); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).pools.post(addPoolReq); + expect(resp).toStrictEqual(expected); + }); + it("should update connection pool", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const poolName = "test"; + const updateReq = { + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin" + }; + const updateReqNock = { + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin" + }; + nock(baseUrl).put(`/v2/databases/${clusterUuid}/pools/${poolName}`, updateReqNock).reply(204); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).pools.byPool_name(poolName).put(updateReq); + expect(resp).toBeUndefined(); + }); + it("should create cluster", async () => { + const createReq = { + name: "backend-cluster", + engine: "pg", + version: "14", + region: "nyc3", + size: "db-s-2vcpu-4gb", + numNodes: 3, + tags: ["production"], + }; + const createReqNock = { + name: "backend-cluster", + engine: "pg", + version: "14", + region: "nyc3", + size: "db-s-2vcpu-4gb", + num_nodes: 3, + tags: ["production"], + }; + const expected = { + database: { + id: "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + name: "backend-cluster", + engine: "pg", + version: "14", + semantic_version: "14.5", + connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@backend-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + private_connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@private-backend-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "private-backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + users: [ + { name: "doadmin", role: "primary", password: "wv78n3zpz42xezdk" } + ], + db_names: ["defaultdb"], + num_nodes: 3, + region: "nyc3", + status: "creating", + created_at: "2019-01-11T18:37:36Z", + maintenance_window: { + day: "saturday", + hour: "08:45:12", + pending: true, + description: [ + "Update TimescaleDB to version 1.2.1", + "Upgrade to PostgreSQL 11.2 and 10.7 bugfix releases", + ], + }, + size: "db-s-2vcpu-4gb", + tags: ["production"], + private_network_uuid: "d455e75d-4858-4eec-8c95-da2f0a5f93a7", + version_end_of_life: "2023-11-09T00:00:00Z", + version_end_of_availability: "2023-05-09T00:00:00Z", + } + }; + const typeExpected = { + database: { + id: "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + name: "backend-cluster", + engine: "pg", + version: "14", + semanticVersion: "14.5", + connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@backend-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + privateConnection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@private-backend-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "private-backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + users: [ + { name: "doadmin", role: "primary", password: "wv78n3zpz42xezdk" } + ], + dbNames: ["defaultdb"], + numNodes: 3, + region: "nyc3", + status: "creating", + createdAt: new Date("2019-01-11T18:37:36Z"), + maintenanceWindow: { + day: "saturday", + hour: "08:45:12", + pending: true, + description: [ + "Update TimescaleDB to version 1.2.1", + "Upgrade to PostgreSQL 11.2 and 10.7 bugfix releases", + ], + }, + size: "db-s-2vcpu-4gb", + tags: ["production"], + privateNetworkUuid: "d455e75d-4858-4eec-8c95-da2f0a5f93a7", + versionEndOfLife: "2023-11-09T00:00:00Z", + versionEndOfAvailability: "2023-05-09T00:00:00Z", + } + }; + nock(baseUrl).post("/v2/databases", createReqNock).reply(201, expected); + const resp = await client.v2.databases.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should add user", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const addUserReq = { name: "app-01" }; + const addUserReqNock = { name: "app-01" }; + const expected = { + user: { name: "app-01", role: "normal", password: "jge5lfxtzhx42iff" } + }; + const typeExpected = { + user: { name: "app-01", role: "normal", password: "jge5lfxtzhx42iff" } + }; + nock(baseUrl).post(`/v2/databases/${clusterUuid}/users`, addUserReqNock).reply(201, expected); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).users.post(addUserReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should update major version", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const updateReq = { version: "14" }; + const updateReqNock = { version: "14" }; + nock(baseUrl).put(`/v2/databases/${clusterUuid}/upgrade`, updateReqNock).reply(204); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).upgrade.put(updateReq); + expect(resp).toBeUndefined(); + }); + it("should create replica", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const createReplicaReq = { + name: "read-nyc3-01", + region: "nyc3", + size: "db-s-2vcpu-4gb" + }; + const createReplicaReqNock = { + name: "read-nyc3-01", + region: "nyc3", + size: "db-s-2vcpu-4gb" + }; + const expected = { + replica: { + name: "read-nyc3-01", + connection: { + uri: "", + database: "defaultdb", + host: "read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + private_connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@private-read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "private-read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + region: "nyc3", + status: "online", + created_at: "2019-01-11T18:37:36Z", + } + }; + const typeExpected = { + replica: { + name: "read-nyc3-01", + connection: { + uri: "", + database: "defaultdb", + host: "read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + privateConnection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@private-read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "private-read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + region: "nyc3", + status: "online", + createdAt: new Date("2019-01-11T18:37:36Z"), + } + }; + nock(baseUrl).post(`/v2/databases/${clusterUuid}/replicas`, createReplicaReqNock).reply(201, expected); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).replicas.post(createReplicaReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should promote replica", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const replicaName = "postgres_nyc_replica"; + nock(baseUrl).put(`/v2/databases/${clusterUuid}/replicas/${replicaName}/promote`).reply(204); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).replicas.byReplica_name(replicaName).promote.put(); + expect(resp).toBeUndefined(); + }); + it("should get user", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const userName = "app-01"; + const expected = { + user: { name: "app-01", role: "normal", password: "jge5lfxtzhx42iff" } + }; + const typeExpected = { + user: { name: "app-01", role: "normal", password: "jge5lfxtzhx42iff" } + }; + nock(baseUrl).get(`/v2/databases/${clusterUuid}/users/${userName}`).reply(200, expected); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).users.byUsername(userName).get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should list databases", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const expected = { dbs: [{ name: "alpha" }, { name: "defaultdb" }] }; + const typeExpected = { dbs: [{ name: "alpha" }, { name: "defaultdb" }] }; + nock(baseUrl).get(`/v2/databases/${clusterUuid}/dbs`).reply(200, expected); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).dbs.get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should list backups", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const expected = { + backups: [ + { created_at: "2019-01-11T18:42:27Z", size_gigabytes: 0.03357696 }, + { created_at: "2019-01-12T18:42:29Z", size_gigabytes: 0.03364864 }, + ] + }; + const typeExpected = { + backups: [ + { createdAt: new Date("2019-01-11T18:42:27Z"), sizeGigabytes: 0.03357696 }, + { createdAt: new Date("2019-01-12T18:42:29Z"), sizeGigabytes: 0.03364864 }, + ] + }; + nock(baseUrl).get(`/v2/databases/${clusterUuid}/backups`).reply(200, expected); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).backups.get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should delete database", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const databaseName = "alpha"; + nock(baseUrl).delete(`/v2/databases/${clusterUuid}/dbs/${databaseName}`).reply(204); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).dbs.byDatabase_name(databaseName).delete(); + expect(resp).toBeUndefined(); + }); + it("should delete connection pool", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const poolName = "backend-pool"; + nock(baseUrl).delete(`/v2/databases/${clusterUuid}/pools/${poolName}`).reply(204); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).pools.byPool_name(poolName).delete(); + expect(resp).toBeUndefined(); + }); + it("should delete user", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const userName = "app-01"; + nock(baseUrl).delete(`/v2/databases/${clusterUuid}/users/${userName}`).reply(204); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).users.byUsername(userName).delete(); + expect(resp).toBeUndefined(); + }); + it("should destroy replica", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const replicaName = "read_nyc_3"; + nock(baseUrl).delete(`/v2/databases/${clusterUuid}/replicas/${replicaName}`).reply(204); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).replicas.byReplica_name(replicaName).delete(); + expect(resp).toBeUndefined(); + }); + it("should get metrics credentials", async () => { + const expected = { + credentials: { + basic_auth_username: "username", + basic_auth_password: "password", + } + }; + const typeExpected = { + credentials: { + additionalData: { + basic_auth_username: "username", + basic_auth_password: "password", + } + } + }; + nock(baseUrl).get("/v2/databases/metrics/credentials").reply(200, expected); + const resp = await client.v2.databases.metrics.credentials.get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should update metrics credentials", async () => { + const updateReq = { + credentials: { + basicAuthUsername: "new_username", + basicAuthPassword: "new_password", + } + }; + const updateReqNock = { + credentials: { + basic_auth_username: "new_username", + basic_auth_password: "new_password", + } + }; + nock(baseUrl).put("/v2/databases/metrics/credentials", updateReqNock).reply(204); + const resp = await client.v2.databases.metrics.credentials.put(updateReq); + expect(resp).toBeUndefined(); + }); +}); diff --git a/tests/mocked/databases.test.ts b/tests/mocked/databases.test.ts new file mode 100644 index 000000000..5e1077705 --- /dev/null +++ b/tests/mocked/databases.test.ts @@ -0,0 +1,476 @@ +// databases.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { DatabasesPostRequestBody} from "../../src/dots/v2/databases/index.js"; +import { Connection_pool , Connection_pool_update, Database_replica} from "../../src/dots/models/index.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Databases API", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should add database", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const addReq = { name: "alpha" }; + const addReqNock = { name: "alpha" }; + + const expected = { db: { name: "alpha" } }; + const typeExpected = { db: { name: "alpha" } }; + + nock(baseUrl).post(`/v2/databases/${clusterUuid}/dbs`, addReqNock).reply(201, expected); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).dbs.post(addReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should add connection pool", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const addPoolReq: Connection_pool = { + name: "backend-pool", + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin", + }; + + const addPoolReqNock = { + name: "backend-pool", + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin", + }; + + const expected = { + pool: { + user: "doadmin", + name: "backend-pool", + size: 10, + db: "defaultdb", + mode: "transaction", + connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@backend-do-user-19081923-0.db.ondigitalocean.com:25061/backend-pool?sslmode=require", + database: "backend-pool", + host: "backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25061, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + } + }; + + nock(baseUrl).post(`/v2/databases/${clusterUuid}/pools`, addPoolReqNock).reply(201, expected); + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).pools.post(addPoolReq); + expect(resp).toStrictEqual(expected); + }); + + it("should update connection pool", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const poolName = "test"; + const updateReq :Connection_pool_update = { + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin" + }; + + const updateReqNock = { + mode: "transaction", + size: 10, + db: "defaultdb", + user: "doadmin" + }; + + nock(baseUrl).put(`/v2/databases/${clusterUuid}/pools/${poolName}`, updateReqNock).reply(204); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).pools.byPool_name(poolName).put(updateReq); + expect(resp).toBeUndefined(); + }); + + it("should create cluster", async () => { + const createReq: DatabasesPostRequestBody = { + name: "backend-cluster", + engine: "pg", + version: "14", + region: "nyc3", + size: "db-s-2vcpu-4gb", + numNodes: 3, + tags: ["production"], + }; + + const createReqNock = { + name: "backend-cluster", + engine: "pg", + version: "14", + region: "nyc3", + size: "db-s-2vcpu-4gb", + num_nodes: 3, + tags: ["production"], + }; + + const expected = { + database: { + id: "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + name: "backend-cluster", + engine: "pg", + version: "14", + semantic_version: "14.5", + connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@backend-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + private_connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@private-backend-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "private-backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + users: [ + { name: "doadmin", role: "primary", password: "wv78n3zpz42xezdk" } + ], + db_names: ["defaultdb"], + num_nodes: 3, + region: "nyc3", + status: "creating", + created_at: "2019-01-11T18:37:36Z", + maintenance_window: { + day: "saturday", + hour: "08:45:12", + pending: true, + description: [ + "Update TimescaleDB to version 1.2.1", + "Upgrade to PostgreSQL 11.2 and 10.7 bugfix releases", + ], + }, + size: "db-s-2vcpu-4gb", + tags: ["production"], + private_network_uuid: "d455e75d-4858-4eec-8c95-da2f0a5f93a7", + version_end_of_life: "2023-11-09T00:00:00Z", + version_end_of_availability: "2023-05-09T00:00:00Z", + } + }; + + const typeExpected = { + database: { + id: "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30", + name: "backend-cluster", + engine: "pg", + version: "14", + semanticVersion: "14.5", + connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@backend-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + privateConnection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@private-backend-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "private-backend-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + users: [ + { name: "doadmin", role: "primary", password: "wv78n3zpz42xezdk" } + ], + dbNames: ["defaultdb"], + numNodes: 3, + region: "nyc3", + status: "creating", + createdAt: new Date("2019-01-11T18:37:36Z"), + maintenanceWindow: { + day: "saturday", + hour: "08:45:12", + pending: true, + description: [ + "Update TimescaleDB to version 1.2.1", + "Upgrade to PostgreSQL 11.2 and 10.7 bugfix releases", + ], + }, + size: "db-s-2vcpu-4gb", + tags: ["production"], + privateNetworkUuid: "d455e75d-4858-4eec-8c95-da2f0a5f93a7", + versionEndOfLife: "2023-11-09T00:00:00Z", + versionEndOfAvailability: "2023-05-09T00:00:00Z", + } + }; + + nock(baseUrl).post("/v2/databases", createReqNock).reply(201, expected); + + const resp = await client.v2.databases.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should add user", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const addUserReq = { name: "app-01" }; + const addUserReqNock = { name: "app-01" }; + + const expected = { + user: { name: "app-01", role: "normal", password: "jge5lfxtzhx42iff" } + }; + + const typeExpected = { + user: { name: "app-01", role: "normal", password: "jge5lfxtzhx42iff" } + }; + + nock(baseUrl).post(`/v2/databases/${clusterUuid}/users`, addUserReqNock).reply(201, expected); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).users.post(addUserReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should update major version", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const updateReq = { version: "14" }; + const updateReqNock = { version: "14" }; + + nock(baseUrl).put(`/v2/databases/${clusterUuid}/upgrade`, updateReqNock).reply(204); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).upgrade.put(updateReq); + expect(resp).toBeUndefined(); + }); + + it("should create replica", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const createReplicaReq: Database_replica = { + name: "read-nyc3-01", + region: "nyc3", + size: "db-s-2vcpu-4gb" + }; + + const createReplicaReqNock = { + name: "read-nyc3-01", + region: "nyc3", + size: "db-s-2vcpu-4gb" + }; + + const expected = { + replica: { + name: "read-nyc3-01", + connection: { + uri: "", + database: "defaultdb", + host: "read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + private_connection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@private-read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "private-read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + region: "nyc3", + status: "online", + created_at: "2019-01-11T18:37:36Z", + } + }; + + const typeExpected = { + replica: { + name: "read-nyc3-01", + connection: { + uri: "", + database: "defaultdb", + host: "read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + privateConnection: { + uri: "postgres://doadmin:wv78n3zpz42xezdk@private-read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require", + database: "", + host: "private-read-nyc3-01-do-user-19081923-0.db.ondigitalocean.com", + port: 25060, + user: "doadmin", + password: "wv78n3zpz42xezdk", + ssl: true, + }, + region: "nyc3", + status: "online", + createdAt: new Date("2019-01-11T18:37:36Z"), + } + }; + + nock(baseUrl).post(`/v2/databases/${clusterUuid}/replicas`, createReplicaReqNock).reply(201, expected); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).replicas.post(createReplicaReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should promote replica", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const replicaName = "postgres_nyc_replica"; + + nock(baseUrl).put(`/v2/databases/${clusterUuid}/replicas/${replicaName}/promote`).reply(204); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).replicas.byReplica_name(replicaName).promote.put(); + expect(resp).toBeUndefined(); + }); + + it("should get user", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const userName = "app-01"; + + const expected = { + user: { name: "app-01", role: "normal", password: "jge5lfxtzhx42iff" } + }; + + const typeExpected = { + user: { name: "app-01", role: "normal", password: "jge5lfxtzhx42iff" } + }; + + nock(baseUrl).get(`/v2/databases/${clusterUuid}/users/${userName}`).reply(200, expected); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).users.byUsername(userName).get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should list databases", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + + const expected = { dbs: [{ name: "alpha" }, { name: "defaultdb" }] }; + const typeExpected = { dbs: [{ name: "alpha" }, { name: "defaultdb" }] }; + + nock(baseUrl).get(`/v2/databases/${clusterUuid}/dbs`).reply(200, expected); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).dbs.get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should list backups", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + + const expected = { + backups: [ + { created_at: "2019-01-11T18:42:27Z", size_gigabytes: 0.03357696 }, + { created_at: "2019-01-12T18:42:29Z", size_gigabytes: 0.03364864 }, + ] + }; + + const typeExpected = { + backups: [ + { createdAt: new Date("2019-01-11T18:42:27Z"), sizeGigabytes: 0.03357696 }, + { createdAt: new Date("2019-01-12T18:42:29Z"), sizeGigabytes: 0.03364864 }, + ] + }; + + nock(baseUrl).get(`/v2/databases/${clusterUuid}/backups`).reply(200, expected); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).backups.get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should delete database", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const databaseName = "alpha"; + + nock(baseUrl).delete(`/v2/databases/${clusterUuid}/dbs/${databaseName}`).reply(204); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).dbs.byDatabase_name(databaseName).delete(); + expect(resp).toBeUndefined(); + }); + + it("should delete connection pool", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const poolName = "backend-pool"; + + nock(baseUrl).delete(`/v2/databases/${clusterUuid}/pools/${poolName}`).reply(204); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).pools.byPool_name(poolName).delete(); + expect(resp).toBeUndefined(); + }); + + it("should delete user", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const userName = "app-01"; + + nock(baseUrl).delete(`/v2/databases/${clusterUuid}/users/${userName}`).reply(204); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).users.byUsername(userName).delete(); + expect(resp).toBeUndefined(); + }); + + it("should destroy replica", async () => { + const clusterUuid = "9cc10173-e9ea-4176-9dbc-a4cee4c4ff30"; + const replicaName = "read_nyc_3"; + + nock(baseUrl).delete(`/v2/databases/${clusterUuid}/replicas/${replicaName}`).reply(204); + + const resp = await client.v2.databases.byDatabase_cluster_uuid(clusterUuid).replicas.byReplica_name(replicaName).delete(); + expect(resp).toBeUndefined(); + }); + + it("should get metrics credentials", async () => { + const expected = { + credentials: { + basic_auth_username: "username", + basic_auth_password: "password", + } + }; + + const typeExpected = { + credentials: { + additionalData: { + basic_auth_username: "username", + basic_auth_password: "password", + } + } + }; + + nock(baseUrl).get("/v2/databases/metrics/credentials").reply(200, expected); + + const resp = await client.v2.databases.metrics.credentials.get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should update metrics credentials", async () => { + const updateReq = { + credentials: { + basicAuthUsername: "new_username", + basicAuthPassword: "new_password", + } + }; + + const updateReqNock = { + credentials: { + basic_auth_username: "new_username", + basic_auth_password: "new_password", + } + }; + + nock(baseUrl).put("/v2/databases/metrics/credentials", updateReqNock).reply(204); + + const resp = await client.v2.databases.metrics.credentials.put(updateReq); + expect(resp).toBeUndefined(); + }); +}); \ No newline at end of file diff --git a/tests/mocked/domain.test.js b/tests/mocked/domain.test.js new file mode 100644 index 000000000..100040d0e --- /dev/null +++ b/tests/mocked/domain.test.js @@ -0,0 +1,406 @@ +// domains.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Domains API", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should create domain", async () => { + const createReq = { name: "clienttest.com" }; + const createReqNock = { name: "clienttest.com" }; + const expected = { + domain: { + name: "clienttest.com", + ttl: 1800, + zone_file: "" + } + }; + const typeExpected = { + domain: { + name: "clienttest.com", + ttl: 1800, + zoneFile: "" + } + }; + nock(baseUrl).post("/v2/domains", createReqNock).reply(201, expected); + const resp = await client.v2.domains.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should get domain", async () => { + const domainName = "clienttest.com"; + const expected = { + domain: { + name: "clienttest.com", + ttl: 1800, + zone_file: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + } + }; + const typeExpected = { + domain: { + name: "clienttest.com", + ttl: 1800, + zoneFile: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + } + }; + nock(baseUrl).get(`/v2/domains/${domainName}`).reply(200, expected); + const resp = await client.v2.domains.byDomain_name(domainName).get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should list domains with pagination", async () => { + const expected = { + domains: [ + { + name: "clienttest.com", + ttl: 1800, + zone_file: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + }, + ], + links: { + pages: { + next: "https://api.digitalocean.com/v2/domains?page=2&per_page=20", + last: "https://api.digitalocean.com/v2/domains?page=6&per_page=20", + } + }, + meta: { total: 6 }, + }; + const typeExpected = { + domains: [ + { + name: "clienttest.com", + ttl: 1800, + zoneFile: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + }, + ], + links: { + pages: { + additionalData: { + next: "https://api.digitalocean.com/v2/domains?page=2&per_page=20", + last: "https://api.digitalocean.com/v2/domains?page=6&per_page=20", + } + } + }, + meta: { total: 6 }, + }; + nock(baseUrl) + .get("/v2/domains") + .query({ per_page: 20, page: 1 }) + .reply(200, expected); + const resp = await client.v2.domains.get({ + queryParameters: { perPage: 20, page: 1 } + }); + expect(resp).toStrictEqual(typeExpected); + }); + it("should delete domain", async () => { + const domainName = "testtclient.com"; + nock(baseUrl).delete(`/v2/domains/${domainName}`).reply(204); + const resp = await client.v2.domains.byDomain_name(domainName).delete(); + expect(resp).toBeUndefined(); + }); + it("should create record", async () => { + const domainName = "ec.com"; + const createRecordReq = { + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }; + const createRecordNock = { + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }; + const expected = { + domain_record: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + } + }; + const typeExpected = { + domainRecord: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + } + }; + nock(baseUrl).post(`/v2/domains/${domainName}/records`, createRecordNock).reply(201, expected); + const resp = await client.v2.domains.byDomain_name(domainName).records.post(createRecordReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should list domains", async () => { + const expected = { + domains: [ + { + name: "clienttest.com", + ttl: 1800, + zone_file: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + }, + ], + links: {}, + meta: { total: 1 }, + }; + const typeExpected = { + domains: [ + { + name: "clienttest.com", + ttl: 1800, + zoneFile: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + }, + ], + links: {}, + meta: { total: 1 }, + }; + nock(baseUrl).get("/v2/domains").reply(200, expected); + const resp = await client.v2.domains.get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should list records", async () => { + const domainName = "ec.com"; + const expected = { + domain_records: [ + { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + ], + links: {}, + meta: { total: 1 }, + }; + const typeExpected = { + domainRecords: [ + { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + ], + links: {}, + meta: { total: 1 }, + }; + nock(baseUrl).get(`/v2/domains/${domainName}/records`).reply(200, expected); + const resp = await client.v2.domains.byDomain_name(domainName).records.get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should list records with pagination", async () => { + const domainName = "ec.com"; + const expected = { + domain_records: [ + { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + ], + links: { + pages: { + next: "https://api.digitalocean.com/v2/domains/ec.com/records?page=2&per_page=20", + last: "https://api.digitalocean.com/v2/domains/ec.com/records?page=3&per_page=20", + } + }, + meta: { total: 6 }, + }; + const typeExpected = { + domainRecords: [ + { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + ], + links: { + pages: { + additionalData: { + next: "https://api.digitalocean.com/v2/domains/ec.com/records?page=2&per_page=20", + last: "https://api.digitalocean.com/v2/domains/ec.com/records?page=3&per_page=20", + } + } + }, + meta: { total: 6 }, + }; + nock(baseUrl) + .get(`/v2/domains/${domainName}/records`) + .query({ per_page: 20, page: 1 }) + .reply(200, expected); + const resp = await client.v2.domains.byDomain_name(domainName).records.get({ + queryParameters: { perPage: 20, page: 1 } + }); + expect(resp).toStrictEqual(typeExpected); + }); + it("should get record", async () => { + const domainName = "ec.com"; + const recordId = 324119029; + const expected = { + domain_record: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + }; + const typeExpected = { + domainRecord: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + }; + nock(baseUrl).get(`/v2/domains/${domainName}/records/${recordId}`).reply(200, expected); + const resp = await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should update record", async () => { + const domainName = "ec.com"; + const recordId = 324119029; + const updateReq = { name: "ec.com", type: "A" }; + const updateReqNock = { name: "ec.com", type: "A" }; + const expected = { + domain_record: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + }; + const typeExpected = { + domainRecord: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + }; + nock(baseUrl).put(`/v2/domains/${domainName}/records/${recordId}`, updateReqNock).reply(200, expected); + const resp = await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).put(updateReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should patch record", async () => { + const domainName = "ec.com"; + const recordId = 324119029; + const patchReq = { name: "ec.com", type: "A" }; + const patchReqNock = { name: "ec.com", type: "A" }; + const expected = { + domain_record: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + }; + const typeExpected = { + domainRecord: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + }; + nock(baseUrl).patch(`/v2/domains/${domainName}/records/${recordId}`, patchReqNock).reply(200, expected); + const resp = await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).patch(patchReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should delete record", async () => { + const domainName = "ec.com"; + const recordId = 324119029; + nock(baseUrl).delete(`/v2/domains/${domainName}/records/${recordId}`).reply(204); + const resp = await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).delete(); + expect(resp).toBeUndefined(); + }); +}); diff --git a/tests/mocked/domain.test.ts b/tests/mocked/domain.test.ts new file mode 100644 index 000000000..5e4da9902 --- /dev/null +++ b/tests/mocked/domain.test.ts @@ -0,0 +1,461 @@ +// domains.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Domain } from "../../src/dots/models/index.js"; +import { Domain_record_a } from "../../src/dots/models/index.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Domains API", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should create domain", async () => { + const createReq: Domain = { name: "clienttest.com" }; + const createReqNock = { name: "clienttest.com" }; + + const expected = { + domain: { + name: "clienttest.com", + ttl: 1800, + zone_file: "" + } + }; + + const typeExpected = { + domain: { + name: "clienttest.com", + ttl: 1800, + zoneFile: "" + } + }; + + nock(baseUrl).post("/v2/domains", createReqNock).reply(201, expected); + + const resp = await client.v2.domains.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should get domain", async () => { + const domainName = "clienttest.com"; + const expected = { + domain: { + name: "clienttest.com", + ttl: 1800, + zone_file: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + } + }; + + const typeExpected = { + domain: { + name: "clienttest.com", + ttl: 1800, + zoneFile: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + } + }; + + nock(baseUrl).get(`/v2/domains/${domainName}`).reply(200, expected); + + const resp = await client.v2.domains.byDomain_name(domainName).get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should list domains with pagination", async () => { + const expected = { + domains: [ + { + name: "clienttest.com", + ttl: 1800, + zone_file: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + }, + ], + links: { + pages: { + next: "https://api.digitalocean.com/v2/domains?page=2&per_page=20", + last: "https://api.digitalocean.com/v2/domains?page=6&per_page=20", + } + }, + meta: { total: 6 }, + }; + + const typeExpected = { + domains: [ + { + name: "clienttest.com", + ttl: 1800, + zoneFile: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + }, + ], + links: { + pages: { + additionalData: { + next: "https://api.digitalocean.com/v2/domains?page=2&per_page=20", + last: "https://api.digitalocean.com/v2/domains?page=6&per_page=20", + } + } + }, + meta: { total: 6 }, + }; + + nock(baseUrl) + .get("/v2/domains") + .query({ per_page: 20, page: 1 }) + .reply(200, expected); + + const resp = await client.v2.domains.get({ + queryParameters: { perPage: 20, page: 1 } + }); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should delete domain", async () => { + const domainName = "testtclient.com"; + + nock(baseUrl).delete(`/v2/domains/${domainName}`).reply(204); + + const resp = await client.v2.domains.byDomain_name(domainName).delete(); + expect(resp).toBeUndefined(); + }); + + it("should create record", async () => { + const domainName = "ec.com"; + const createRecordReq : Domain_record_a = { + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }; + + const createRecordNock = { + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }; + + const expected = { + domain_record: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + } + }; + + const typeExpected = { + domainRecord: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + } + }; + + nock(baseUrl).post(`/v2/domains/${domainName}/records`, createRecordNock).reply(201, expected); + const resp = await client.v2.domains.byDomain_name(domainName).records.post(createRecordReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should list domains", async () => { + const expected = { + domains: [ + { + name: "clienttest.com", + ttl: 1800, + zone_file: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + }, + ], + links: {}, + meta: { total: 1 }, + }; + + const typeExpected = { + domains: [ + { + name: "clienttest.com", + ttl: 1800, + zoneFile: `$ORIGIN clienttest.com.\n$TTL 1800\nclienttest.com. IN SOA ns1.digitalocean.com. hostmaster.clienttest.com. 1657812556 10800 3600 604800 1800\nclienttest.com. 1800 IN NS ns1.digitalocean.com. \nclienttest.com. 1800 IN NS ns2.digitalocean.com.\nclienttest.com. 1800 IN NS ns3.digitalocean.com.\n`, + }, + ], + links: {}, + meta: { total: 1 }, + }; + + nock(baseUrl).get("/v2/domains").reply(200, expected); + + const resp = await client.v2.domains.get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should list records", async () => { + const domainName = "ec.com"; + const expected = { + domain_records: [ + { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + ], + links: {}, + meta: { total: 1 }, + }; + + const typeExpected = { + domainRecords: [ + { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + ], + links: {}, + meta: { total: 1 }, + }; + + nock(baseUrl).get(`/v2/domains/${domainName}/records`).reply(200, expected); + + const resp = await client.v2.domains.byDomain_name(domainName).records.get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should list records with pagination", async () => { + const domainName = "ec.com"; + const expected = { + domain_records: [ + { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + ], + links: { + pages: { + next: "https://api.digitalocean.com/v2/domains/ec.com/records?page=2&per_page=20", + last: "https://api.digitalocean.com/v2/domains/ec.com/records?page=3&per_page=20", + } + }, + meta: { total: 6 }, + }; + + const typeExpected = { + domainRecords: [ + { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + ], + links: { + pages: { + additionalData : { + next: "https://api.digitalocean.com/v2/domains/ec.com/records?page=2&per_page=20", + last: "https://api.digitalocean.com/v2/domains/ec.com/records?page=3&per_page=20", + } + } + }, + meta: { total: 6 }, + }; + + nock(baseUrl) + .get(`/v2/domains/${domainName}/records`) + .query({ per_page: 20, page: 1 }) + .reply(200, expected); + + const resp = await client.v2.domains.byDomain_name(domainName).records.get({ + queryParameters: { perPage: 20, page: 1 } + }); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should get record", async () => { + const domainName = "ec.com"; + const recordId = 324119029; + + const expected = { + domain_record: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + }; + + const typeExpected = { + domainRecord: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + }; + + nock(baseUrl).get(`/v2/domains/${domainName}/records/${recordId}`).reply(200, expected); + + const resp = await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should update record", async () => { + const domainName = "ec.com"; + const recordId = 324119029; + const updateReq = { name: "ec.com", type: "A" }; + const updateReqNock = { name: "ec.com", type: "A" }; + + const expected = { + domain_record: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + }; + + const typeExpected = { + domainRecord: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + }; + + nock(baseUrl).put(`/v2/domains/${domainName}/records/${recordId}`, updateReqNock).reply(200, expected); + + const resp = await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).put(updateReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should patch record", async () => { + const domainName = "ec.com"; + const recordId = 324119029; + const patchReq = { name: "ec.com", type: "A" }; + const patchReqNock = { name: "ec.com", type: "A" }; + + const expected = { + domain_record: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: null, + port: null, + ttl: 1800, + weight: null, + flags: null, + tag: null, + }, + }; + + const typeExpected = { + domainRecord: { + id: 324119029, + type: "A", + name: "ec.com", + data: "162.10.66.0", + priority: undefined, + port: undefined, + ttl: 1800, + weight: undefined, + flags: undefined, + tag: undefined, + }, + }; + + nock(baseUrl).patch(`/v2/domains/${domainName}/records/${recordId}`, patchReqNock).reply(200, expected); + + const resp = await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).patch(patchReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should delete record", async () => { + const domainName = "ec.com"; + const recordId = 324119029; + + nock(baseUrl).delete(`/v2/domains/${domainName}/records/${recordId}`).reply(204); + + const resp = await client.v2.domains.byDomain_name(domainName).records.byDomain_record_id(recordId).delete(); + expect(resp).toBeUndefined(); + }); +}); \ No newline at end of file diff --git a/tests/mocked/droplet.test.js b/tests/mocked/droplet.test.js index b7643837b..efd761566 100644 --- a/tests/mocked/droplet.test.js +++ b/tests/mocked/droplet.test.js @@ -1416,62 +1416,6 @@ describe("Droplets API", () => { const resp = await client.v2.droplets.delete({ queryParameters: { tagName } }); expect(resp).toBeUndefined(); }); - // it("mocks the droplets list backups operation", async () => { - // const expected = { - // backups: [ - // { - // id: 67539192, - // name: "web-01- 2020-07-29", - // distribution: "Ubuntu", - // slug: null, - // public: false, - // regions: ["nyc3"], - // created_at: "2020-07-29T01:44:35Z", - // min_disk_size: 50, - // size_gigabytes: 2.34, - // type: "backup", - // }, - // ], - // links: {}, - // meta: { - // total: 1, - // }, - // }; - // const dropletId = 1; - // nock(baseUrl) - // .get(`/v2/droplets/${dropletId}/backups`) - // .reply(200, expected); - // const resp = await client.v2.droplets.byDroplet_id(dropletId).backups.get(); - // console.log(JSON.stringify(resp)); - // expect(resp).toEqual(expected); - // }); - // it("mocks the droplets list kernels operation", async () => { - // const expected = { - // kernels: [ - // { - // id: 7515, - // name: "DigitalOcean GrubLoader v0.2 (20160714)", - // version: "2016.07.13-DigitalOcean_loader_Ubuntu", - // }, - // ], - // links: { - // pages: { - // next: "https://api.digitalocean.com/v2/droplets/3164444/kernels?page=2&per_page=1", - // last: "https://api.digitalocean.com/v2/droplets/3164444/kernels?page=171&per_page=1", - // }, - // }, - // meta: { total: 171 }, - // }; - // const dropletId = 3929391; - // nock(baseUrl) - // .get(`/v2/droplets/${dropletId}/kernels`) - // .reply(200, expected); - // const resp = await client.v2.droplets - // .byDroplet_id(dropletId) - // .kernels.get(); - // console.log(JSON.stringify(resp)) - // expect(resp).toEqual(expected); - // }); it("mocks the droplets list firewalls operation", async () => { const expected = { firewalls: [ @@ -2102,24 +2046,4 @@ describe("Droplets API", () => { .post(); expect(resp).toBeUndefined(); }); - // it('mocks the droplets list all neighbors operation', async () => { - // const expected = { - // neighbor_ids: [ - // [168671828, 168663509, 168671815], - // [168671883, 168671750], - // ], - // }; - // const typeExpected = { - // neighborIds: [ - // [168671828, 168663509, 168671815], - // [168671883, 168671750], - // ], - // }; - // nock(baseUrl) - // .get('/v2/reports/droplet_neighbors_ids') - // .reply(200, expected); - // const resp = await client.v2.reports.droplet_neighbors_ids.get(); - // console.log(JSON.stringify(resp)); - // expect(resp).toEqual(typeExpected); - // }); }); diff --git a/tests/mocked/droplet.test.ts b/tests/mocked/droplet.test.ts index 6e98a086f..1cdccd039 100644 --- a/tests/mocked/droplet.test.ts +++ b/tests/mocked/droplet.test.ts @@ -1430,66 +1430,6 @@ describe("Droplets API", () => { const resp = await client.v2.droplets.delete({ queryParameters : {tagName} }); expect(resp).toBeUndefined(); }); - - // it("mocks the droplets list backups operation", async () => { - // const expected = { - // backups: [ - // { - // id: 67539192, - // name: "web-01- 2020-07-29", - // distribution: "Ubuntu", - // slug: null, - // public: false, - // regions: ["nyc3"], - // created_at: "2020-07-29T01:44:35Z", - // min_disk_size: 50, - // size_gigabytes: 2.34, - // type: "backup", - // }, - // ], - // links: {}, - // meta: { - // total: 1, - // }, - // }; - - // const dropletId = 1; - // nock(baseUrl) - // .get(`/v2/droplets/${dropletId}/backups`) - // .reply(200, expected); - // const resp = await client.v2.droplets.byDroplet_id(dropletId).backups.get(); - // console.log(JSON.stringify(resp)); - // expect(resp).toEqual(expected); - // }); - // it("mocks the droplets list kernels operation", async () => { - // const expected = { - // kernels: [ - // { - // id: 7515, - // name: "DigitalOcean GrubLoader v0.2 (20160714)", - // version: "2016.07.13-DigitalOcean_loader_Ubuntu", - // }, - // ], - // links: { - // pages: { - // next: "https://api.digitalocean.com/v2/droplets/3164444/kernels?page=2&per_page=1", - // last: "https://api.digitalocean.com/v2/droplets/3164444/kernels?page=171&per_page=1", - // }, - // }, - // meta: { total: 171 }, - // }; - - // const dropletId = 3929391; - // nock(baseUrl) - // .get(`/v2/droplets/${dropletId}/kernels`) - // .reply(200, expected); - // const resp = await client.v2.droplets - // .byDroplet_id(dropletId) - // .kernels.get(); - // console.log(JSON.stringify(resp)) - // expect(resp).toEqual(expected); - // }); - it("mocks the droplets list firewalls operation", async () => { const expected = { firewalls: [ @@ -2144,30 +2084,4 @@ describe("Droplets API", () => { expect(resp).toBeUndefined(); }); - - - // it('mocks the droplets list all neighbors operation', async () => { - // const expected = { - // neighbor_ids: [ - // [168671828, 168663509, 168671815], - // [168671883, 168671750], - // ], - // }; - - // const typeExpected = { - // neighborIds: [ - // [168671828, 168663509, 168671815], - // [168671883, 168671750], - // ], - // }; - - // nock(baseUrl) - // .get('/v2/reports/droplet_neighbors_ids') - // .reply(200, expected); - - // const resp = await client.v2.reports.droplet_neighbors_ids.get(); - // console.log(JSON.stringify(resp)); - // expect(resp).toEqual(typeExpected); - // }); - }); diff --git a/tests/mocked/firewalls.test.js b/tests/mocked/firewalls.test.js new file mode 100644 index 000000000..1ae2ff787 --- /dev/null +++ b/tests/mocked/firewalls.test.js @@ -0,0 +1,539 @@ +// firewalls.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Firewalls API", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should list firewalls", async () => { + const expected = { + firewalls: [ + { + id: "e8721de7-ebd9-46ff-8b8d-f6cd14b2769b", + name: "public-access", + status: "succeeded", + inbound_rules: [], + outbound_rules: [ + { + protocol: "icmp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + { + protocol: "tcp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + { + protocol: "udp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + ], + created_at: "2021-10-11T19:04:13Z", + droplet_ids: [], + tags: ["public-access"], + pending_changes: [], + }, + { + id: "fb6045f1-cf1d-4ca3-bfac-18832663025b", + name: "firewall", + status: "succeeded", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: [ + "4de7ac8b-495b-4884-9a69-1050c6793cd6" + ] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + created_at: "2017-05-23T21:23:59Z", + droplet_ids: [123456], + tags: [], + pending_changes: [], + }, + ], + links: {}, + meta: { total: 2 }, + }; + const typeExpected = { + firewalls: [ + { + id: "e8721de7-ebd9-46ff-8b8d-f6cd14b2769b", + name: "public-access", + status: "succeeded", + inboundRules: [], + outboundRules: [ + { + protocol: "icmp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + { + protocol: "tcp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + { + protocol: "udp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + ], + createdAt: new Date("2021-10-11T19:04:13Z"), + dropletIds: [], + tags: ["public-access"], + pendingChanges: [], + }, + { + id: "fb6045f1-cf1d-4ca3-bfac-18832663025b", + name: "firewall", + status: "succeeded", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: [ + "4de7ac8b-495b-4884-9a69-1050c6793cd6" + ] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + createdAt: new Date("2017-05-23T21:23:59Z"), + dropletIds: [123456], + tags: [], + pendingChanges: [], + }, + ], + links: {}, + meta: { total: 2 }, + }; + nock(baseUrl).get("/v2/firewalls").reply(200, expected); + const resp = await client.v2.firewalls.get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should get firewall", async () => { + const firewallId = "fb6045f1-cf1d-4ca3-bfac-18832663025b"; + const expected = { + firewall: { + id: "fb6045f1-cf1d-4ca3-bfac-18832663025b", + name: "firewall", + status: "succeeded", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + created_at: "2017-05-23T21:23:59Z", + droplet_ids: [123456], + tags: [], + pending_changes: [], + } + }; + const typeExpected = { + firewall: { + id: "fb6045f1-cf1d-4ca3-bfac-18832663025b", + name: "firewall", + status: "succeeded", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + createdAt: new Date("2017-05-23T21:23:59Z"), + dropletIds: [123456], + tags: [], + pendingChanges: [], + } + }; + nock(baseUrl).get(`/v2/firewalls/${firewallId}`).reply(200, expected); + const resp = await client.v2.firewalls.byFirewall_id(firewallId).get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should create firewall", async () => { + const createReq = { + name: "firewall", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + dropletIds: [8043964], + }; + const createReqNock = { + name: "firewall", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + droplet_ids: [8043964], + }; + const expected = { + firewall: { + id: "bb4b2611-3d72-467b-8602-280330ecd65c", + name: "firewall", + status: "waiting", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + created_at: "2017-05-23T21:24:00Z", + droplet_ids: [8043964], + tags: [], + pending_changes: [ + { droplet_id: 8043964, removing: false, status: "waiting" } + ], + } + }; + const typeExpected = { + firewall: { + id: "bb4b2611-3d72-467b-8602-280330ecd65c", + name: "firewall", + status: "waiting", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + createdAt: new Date("2017-05-23T21:24:00Z"), + dropletIds: [8043964], + tags: [], + pendingChanges: [ + { dropletId: 8043964, removing: false, status: "waiting" } + ], + } + }; + nock(baseUrl).post("/v2/firewalls", createReqNock).reply(202, expected); + const resp = await client.v2.firewalls.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should update firewall", async () => { + const firewallId = "bb4b2611-3d72-467b-8602-280330ecd65c"; + const updateReq = { + name: "frontend-firewall", + inboundRules: [ + { + protocol: "tcp", + ports: "8080", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outboundRules: [ + { + protocol: "tcp", + ports: "8080", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + dropletIds: [8043964], + tags: ["frontend"], + }; + const updateReqNock = { + name: "frontend-firewall", + inbound_rules: [ + { + protocol: "tcp", + ports: "8080", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "8080", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + droplet_ids: [8043964], + tags: ["frontend"], + }; + const expected = { + firewall: { + id: "bb4b2611-3d72-467b-8602-280330ecd65c", + name: "frontend-firewall", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + created_at: "2020-05-23T21:24:00Z", + droplet_ids: [8043964], + tags: ["frontend"], + status: "waiting", + pending_changes: [ + { droplet_id: 8043964, removing: false, status: "waiting" } + ], + } + }; + const typeExpected = { + firewall: { + id: "bb4b2611-3d72-467b-8602-280330ecd65c", + name: "frontend-firewall", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + createdAt: new Date("2020-05-23T21:24:00Z"), + dropletIds: [8043964], + tags: ["frontend"], + status: "waiting", + pendingChanges: [ + { dropletId: 8043964, removing: false, status: "waiting" } + ], + } + }; + nock(baseUrl).put(`/v2/firewalls/${firewallId}`, updateReqNock).reply(200, expected); + const resp = await client.v2.firewalls.byFirewall_id(firewallId).put(updateReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should delete firewall", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + nock(baseUrl).delete(`/v2/firewalls/${firewallId}`).reply(204); + const resp = await client.v2.firewalls.byFirewall_id(firewallId).delete(); + expect(resp).toBeUndefined(); + }); + it("should assign droplets", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const assignReq = { dropletIds: [1234, 5678] }; + const assignReqNock = { droplet_ids: [1234, 5678] }; + nock(baseUrl).post(`/v2/firewalls/${firewallId}/droplets`, assignReqNock).reply(204); + const resp = await client.v2.firewalls.byFirewall_id(firewallId).droplets.post(assignReq); + expect(resp).toBeUndefined(); + }); + it("should delete droplets", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const removeReq = { dropletIds: [1234, 5678] }; + const removeReqNock = { droplet_ids: [1234, 5678] }; + nock(baseUrl).delete(`/v2/firewalls/${firewallId}/droplets`, removeReqNock).reply(204); + const resp = await client.v2.firewalls.byFirewall_id(firewallId).droplets.delete(removeReq); + expect(resp).toBeUndefined(); + }); + it("should add tags", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const addTagsReq = { tags: ["frontend"] }; + const addTagsReqNock = { tags: ["frontend"] }; + nock(baseUrl).post(`/v2/firewalls/${firewallId}/tags`, addTagsReqNock).reply(204); + const resp = await client.v2.firewalls.byFirewall_id(firewallId).tags.post(addTagsReq); + expect(resp).toBeUndefined(); + }); + it("should delete tags", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const removeTagsReq = { tags: ["frontend"] }; + const removeTagsReqNock = { tags: ["frontend"] }; + nock(baseUrl).delete(`/v2/firewalls/${firewallId}/tags`, removeTagsReqNock).reply(204); + const resp = await client.v2.firewalls.byFirewall_id(firewallId).tags.delete(removeTagsReq); + expect(resp).toBeUndefined(); + }); + it("should add rules", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const addRulesReq = { + inboundRules: [ + { protocol: "tcp", ports: "3306", sources: { dropletIds: [49696269] } } + ], + outboundRules: [ + { + protocol: "tcp", + ports: "3306", + destinations: { dropletIds: [49696269] }, + } + ], + }; + const addRulesReqNock = { + inbound_rules: [ + { protocol: "tcp", ports: "3306", sources: { droplet_ids: [49696269] } } + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "3306", + destinations: { droplet_ids: [49696269] }, + } + ], + }; + nock(baseUrl).post(`/v2/firewalls/${firewallId}/rules`, addRulesReqNock).reply(204); + const resp = await client.v2.firewalls.byFirewall_id(firewallId).rules.post(addRulesReq); + expect(resp).toBeUndefined(); + }); + it("should delete rules", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const deleteRulesReq = { + inboundRules: [ + { protocol: "tcp", ports: "3306", sources: { dropletIds: [49696269] } } + ], + outboundRules: [ + { + protocol: "tcp", + ports: "3306", + destinations: { dropletIds: [49696269] }, + } + ], + }; + const deleteRulesReqNock = { + inbound_rules: [ + { protocol: "tcp", ports: "3306", sources: { droplet_ids: [49696269] } } + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "3306", + destinations: { droplet_ids: [49696269] }, + } + ], + }; + nock(baseUrl).delete(`/v2/firewalls/${firewallId}/rules`, deleteRulesReqNock).reply(204); + const resp = await client.v2.firewalls.byFirewall_id(firewallId).rules.delete(deleteRulesReq); + expect(resp).toBeUndefined(); + }); +}); diff --git a/tests/mocked/firewalls.test.ts b/tests/mocked/firewalls.test.ts new file mode 100644 index 000000000..07e7e9b8f --- /dev/null +++ b/tests/mocked/firewalls.test.ts @@ -0,0 +1,589 @@ +// firewalls.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Firewall } from "../../src/dots/models/index.js"; +import { Firewall_rules } from "../../src/dots/models/index.js"; +import { DropletsPostRequestBody, DropletsDeleteRequestBody } from "../../src/dots/v2/firewalls/item/droplets/index.js"; +import { TagsPostRequestBody, TagsDeleteRequestBody } from "../../src/dots/v2/firewalls/item/tags/index.js"; + + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Firewalls API", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should list firewalls", async () => { + const expected = { + firewalls: [ + { + id: "e8721de7-ebd9-46ff-8b8d-f6cd14b2769b", + name: "public-access", + status: "succeeded", + inbound_rules: [], + outbound_rules: [ + { + protocol: "icmp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + { + protocol: "tcp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + { + protocol: "udp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + ], + created_at: "2021-10-11T19:04:13Z", + droplet_ids: [], + tags: ["public-access"], + pending_changes: [], + }, + { + id: "fb6045f1-cf1d-4ca3-bfac-18832663025b", + name: "firewall", + status: "succeeded", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: [ + "4de7ac8b-495b-4884-9a69-1050c6793cd6" + ] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + created_at: "2017-05-23T21:23:59Z", + droplet_ids: [123456], + tags: [], + pending_changes: [], + }, + ], + links: {}, + meta: { total: 2 }, + }; + + const typeExpected = { + firewalls: [ + { + id: "e8721de7-ebd9-46ff-8b8d-f6cd14b2769b", + name: "public-access", + status: "succeeded", + inboundRules: [], + outboundRules: [ + { + protocol: "icmp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + { + protocol: "tcp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + { + protocol: "udp", + ports: "0", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + }, + ], + createdAt: new Date("2021-10-11T19:04:13Z"), + dropletIds: [], + tags: ["public-access"], + pendingChanges: [], + }, + { + id: "fb6045f1-cf1d-4ca3-bfac-18832663025b", + name: "firewall", + status: "succeeded", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: [ + "4de7ac8b-495b-4884-9a69-1050c6793cd6" + ] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + createdAt: new Date("2017-05-23T21:23:59Z"), + dropletIds: [123456], + tags: [], + pendingChanges: [], + }, + ], + links: {}, + meta: { total: 2 }, + }; + + nock(baseUrl).get("/v2/firewalls").reply(200, expected); + + const resp = await client.v2.firewalls.get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should get firewall", async () => { + const firewallId = "fb6045f1-cf1d-4ca3-bfac-18832663025b"; + const expected = { + firewall: { + id: "fb6045f1-cf1d-4ca3-bfac-18832663025b", + name: "firewall", + status: "succeeded", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + created_at: "2017-05-23T21:23:59Z", + droplet_ids: [123456], + tags: [], + pending_changes: [], + } + }; + + const typeExpected = { + firewall: { + id: "fb6045f1-cf1d-4ca3-bfac-18832663025b", + name: "firewall", + status: "succeeded", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + createdAt: new Date("2017-05-23T21:23:59Z"), + dropletIds: [123456], + tags: [], + pendingChanges: [], + } + }; + + nock(baseUrl).get(`/v2/firewalls/${firewallId}`).reply(200, expected); + + const resp = await client.v2.firewalls.byFirewall_id(firewallId).get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should create firewall", async () => { + const createReq: Firewall = { + name: "firewall", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + dropletIds: [8043964], + }; + + const createReqNock = { + name: "firewall", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + droplet_ids: [8043964], + }; + + const expected = { + firewall: { + id: "bb4b2611-3d72-467b-8602-280330ecd65c", + name: "firewall", + status: "waiting", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + created_at: "2017-05-23T21:24:00Z", + droplet_ids: [8043964], + tags: [], + pending_changes: [ + { droplet_id: 8043964, removing: false, status: "waiting" } + ], + } + }; + + const typeExpected = { + firewall: { + id: "bb4b2611-3d72-467b-8602-280330ecd65c", + name: "firewall", + status: "waiting", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + createdAt: new Date("2017-05-23T21:24:00Z"), + dropletIds: [8043964], + tags: [], + pendingChanges: [ + { dropletId: 8043964, removing: false, status: "waiting" } + ], + } + }; + + nock(baseUrl).post("/v2/firewalls", createReqNock).reply(202, expected); + + const resp = await client.v2.firewalls.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should update firewall", async () => { + const firewallId = "bb4b2611-3d72-467b-8602-280330ecd65c"; + const updateReq: Firewall = { + name: "frontend-firewall", + inboundRules: [ + { + protocol: "tcp", + ports: "8080", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outboundRules: [ + { + protocol: "tcp", + ports: "8080", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + dropletIds: [8043964], + tags: ["frontend"], + }; + + const updateReqNock = { + name: "frontend-firewall", + inbound_rules: [ + { + protocol: "tcp", + ports: "8080", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "8080", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + droplet_ids: [8043964], + tags: ["frontend"], + }; + + const expected = { + firewall: { + id: "bb4b2611-3d72-467b-8602-280330ecd65c", + name: "frontend-firewall", + inbound_rules: [ + { + protocol: "tcp", + ports: "80", + sources: { + load_balancer_uids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + created_at: "2020-05-23T21:24:00Z", + droplet_ids: [8043964], + tags: ["frontend"], + status: "waiting", + pending_changes: [ + { droplet_id: 8043964, removing: false, status: "waiting" } + ], + } + }; + + const typeExpected = { + firewall: { + id: "bb4b2611-3d72-467b-8602-280330ecd65c", + name: "frontend-firewall", + inboundRules: [ + { + protocol: "tcp", + ports: "80", + sources: { + loadBalancerUids: ["4de7ac8b-495b-4884-9a69-1050c6793cd6"] + }, + }, + { + protocol: "tcp", + ports: "22", + sources: { tags: ["gateway"], addresses: ["18.0.0.0/8"] }, + }, + ], + outboundRules: [ + { + protocol: "tcp", + ports: "80", + destinations: { addresses: ["0.0.0.0/0", "::/0"] }, + } + ], + createdAt: new Date("2020-05-23T21:24:00Z"), + dropletIds: [8043964], + tags: ["frontend"], + status: "waiting", + pendingChanges: [ + { dropletId: 8043964, removing: false, status: "waiting" } + ], + } + }; + + nock(baseUrl).put(`/v2/firewalls/${firewallId}`, updateReqNock).reply(200, expected); + + const resp = await client.v2.firewalls.byFirewall_id(firewallId).put(updateReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should delete firewall", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + + nock(baseUrl).delete(`/v2/firewalls/${firewallId}`).reply(204); + + const resp = await client.v2.firewalls.byFirewall_id(firewallId).delete(); + expect(resp).toBeUndefined(); + }); + + it("should assign droplets", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const assignReq : DropletsPostRequestBody= { dropletIds : [1234, 5678] }; + const assignReqNock = { droplet_ids: [1234, 5678] }; + + nock(baseUrl).post(`/v2/firewalls/${firewallId}/droplets`, assignReqNock).reply(204); + + const resp = await client.v2.firewalls.byFirewall_id(firewallId).droplets.post(assignReq) + expect(resp).toBeUndefined(); + }); + + it("should delete droplets", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const removeReq: DropletsDeleteRequestBody = { dropletIds: [1234, 5678] }; + const removeReqNock = { droplet_ids: [1234, 5678] }; + + nock(baseUrl).delete(`/v2/firewalls/${firewallId}/droplets`, removeReqNock).reply(204); + + const resp = await client.v2.firewalls.byFirewall_id(firewallId).droplets.delete(removeReq); + expect(resp).toBeUndefined(); + }); + + it("should add tags", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const addTagsReq: TagsPostRequestBody = { tags: ["frontend"] }; + const addTagsReqNock = { tags: ["frontend"] }; + + nock(baseUrl).post(`/v2/firewalls/${firewallId}/tags`, addTagsReqNock).reply(204); + + const resp = await client.v2.firewalls.byFirewall_id(firewallId).tags.post(addTagsReq); + expect(resp).toBeUndefined(); + }); + + it("should delete tags", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const removeTagsReq: TagsDeleteRequestBody = { tags: ["frontend"] }; + const removeTagsReqNock = { tags: ["frontend"] }; + + nock(baseUrl).delete(`/v2/firewalls/${firewallId}/tags`, removeTagsReqNock).reply(204); + + const resp = await client.v2.firewalls.byFirewall_id(firewallId).tags.delete(removeTagsReq); + expect(resp).toBeUndefined(); + }); + + it("should add rules", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const addRulesReq: Firewall_rules = { + inboundRules: [ + { protocol: "tcp", ports: "3306", sources: { dropletIds: [49696269] } } + ], + outboundRules: [ + { + protocol: "tcp", + ports: "3306", + destinations: { dropletIds: [49696269] }, + } + ], + }; + + const addRulesReqNock = { + inbound_rules: [ + { protocol: "tcp", ports: "3306", sources: { droplet_ids: [49696269] } } + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "3306", + destinations: { droplet_ids: [49696269] }, + } + ], + }; + + nock(baseUrl).post(`/v2/firewalls/${firewallId}/rules`, addRulesReqNock).reply(204); + + const resp = await client.v2.firewalls.byFirewall_id(firewallId).rules.post(addRulesReq); + expect(resp).toBeUndefined(); + }); + + it("should delete rules", async () => { + const firewallId = "aaa-bbb-111-ccc-222"; + const deleteRulesReq: Firewall_rules = { + inboundRules: [ + { protocol: "tcp", ports: "3306", sources: { dropletIds: [49696269] } } + ], + outboundRules: [ + { + protocol: "tcp", + ports: "3306", + destinations: { dropletIds: [49696269] }, + } + ], + }; + + const deleteRulesReqNock = { + inbound_rules: [ + { protocol: "tcp", ports: "3306", sources: { droplet_ids: [49696269] } } + ], + outbound_rules: [ + { + protocol: "tcp", + ports: "3306", + destinations: { droplet_ids: [49696269] }, + } + ], + }; + + nock(baseUrl).delete(`/v2/firewalls/${firewallId}/rules`, deleteRulesReqNock).reply(204); + + const resp = await client.v2.firewalls.byFirewall_id(firewallId).rules.delete(deleteRulesReq); + expect(resp).toBeUndefined(); + }); +}); \ No newline at end of file diff --git a/tests/mocked/images.test.js b/tests/mocked/images.test.js new file mode 100644 index 000000000..6eddad3ba --- /dev/null +++ b/tests/mocked/images.test.js @@ -0,0 +1,391 @@ +// images.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Images API", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should list images", async () => { + const expected = { + images: [ + { + id: 7555620, + name: "Nifty New Snapshot", + distribution: "Ubuntu", + slug: null, + public: false, + regions: ["nyc2", "nyc3"], + created_at: "2014-11-04T22:23:02Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 7555621, + name: "Another Snapshot", + distribution: "Ubuntu", + slug: null, + public: false, + regions: ["nyc2"], + created_at: "2014-11-04T22:23:02Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 63663980, + name: "20.04 (LTS) x64", + distribution: "Ubuntu", + slug: "ubuntu-20-04-x64", + public: true, + regions: ["nyc2", "nyc3"], + created_at: "2020-05-15T05:47:50Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.36, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 7555621, + name: "A custom image", + distribution: "Arch Linux", + slug: null, + public: false, + regions: ["nyc3"], + created_at: "2014-11-04T22:23:02Z", + type: "custom", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 7555621, + name: "An APP image", + distribution: "Fedora", + slug: null, + public: false, + regions: ["nyc2", "nyc3"], + created_at: "2014-11-04T22:23:02Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 7555621, + name: "A simple tagged image", + distribution: "CentOS", + slug: null, + public: false, + regions: ["nyc2", "nyc3"], + created_at: "2014-11-04T22:23:02Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: ["simple-image"], + status: "available", + error_message: "", + }, + ], + links: { pages: {} }, + meta: { total: 6 }, + }; + const typeExpected = { + images: [ + { + id: 7555620, + name: "Nifty New Snapshot", + distribution: "Ubuntu", + slug: undefined, + public: false, + regions: ["nyc2", "nyc3"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 7555621, + name: "Another Snapshot", + distribution: "Ubuntu", + slug: undefined, + public: false, + regions: ["nyc2"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 63663980, + name: "20.04 (LTS) x64", + distribution: "Ubuntu", + slug: "ubuntu-20-04-x64", + public: true, + regions: ["nyc2", "nyc3"], + createdAt: new Date("2020-05-15T05:47:50Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.36, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 7555621, + name: "A custom image", + distribution: "Arch Linux", + slug: undefined, + public: false, + regions: ["nyc3"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "custom", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 7555621, + name: "An APP image", + distribution: "Fedora", + slug: undefined, + public: false, + regions: ["nyc2", "nyc3"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 7555621, + name: "A simple tagged image", + distribution: "CentOS", + slug: undefined, + public: false, + regions: ["nyc2", "nyc3"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: ["simple-image"], + status: "available", + errorMessage: "", + }, + ], + links: { pages: {} }, + meta: { total: 6 }, + }; + nock(baseUrl).get("/v2/images").reply(200, expected); + const resp = await client.v2.images.get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should get image", async () => { + const imageId = 6918990; + const expected = { + image: { + id: 6918990, + name: "14.04 x64", + distribution: "Ubuntu", + slug: "ubuntu-16-04-x64", + public: true, + regions: [ + "nyc1", + "ams1", + "sfo1", + "nyc2", + "ams2", + "sgp1", + "lon1", + "nyc3", + "ams3", + "nyc3", + ], + created_at: "2014-10-17T20:24:33Z", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + } + }; + const typeExpected = { + image: { + id: 6918990, + name: "14.04 x64", + distribution: "Ubuntu", + slug: "ubuntu-16-04-x64", + public: true, + regions: [ + "nyc1", + "ams1", + "sfo1", + "nyc2", + "ams2", + "sgp1", + "lon1", + "nyc3", + "ams3", + "nyc3", + ], + createdAt: new Date("2014-10-17T20:24:33Z"), + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + } + }; + nock(baseUrl).get(`/v2/images/${imageId}`).reply(200, expected); + const resp = await client.v2.images.byImage_id(imageId).get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should delete image", async () => { + const imageId = 6372321; + nock(baseUrl).delete(`/v2/images/${imageId}`).reply(204); + const resp = await client.v2.images.byImage_id(imageId).delete(); + expect(resp).toBeUndefined(); + }); + it("should update image", async () => { + const imageId = 7938391; + const updateReq = { + name: "Nifty New Snapshot", + distribution: "Ubuntu", + description: " ", + }; + const updateReqNock = { + name: "Nifty New Snapshot", + distribution: "Ubuntu", + description: " ", + }; + const expected = { + image: { + id: 7938391, + name: "new-image-name", + distribution: "Ubuntu", + slug: null, + public: false, + regions: ["nyc3", "nyc3"], + created_at: "2014-11-14T16:44:03Z", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + } + }; + const typeExpected = { + image: { + id: 7938391, + name: "new-image-name", + distribution: "Ubuntu", + slug: undefined, + public: false, + regions: ["nyc3", "nyc3"], + createdAt: new Date("2014-11-14T16:44:03Z"), + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + } + }; + nock(baseUrl).put(`/v2/images/${imageId}`, updateReqNock).reply(200, expected); + const resp = await client.v2.images.byImage_id(imageId).put(updateReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should create custom image", async () => { + const createReq = { + name: "ubuntu-18.04-minimal", + url: "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img", + distribution: "Ubuntu", + region: "nyc3", + description: "Cloud-optimized image w/ small footprint", + tags: ["base-image", "prod"], + }; + const createReqNock = { + name: "ubuntu-18.04-minimal", + url: "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img", + distribution: "Ubuntu", + region: "nyc3", + description: "Cloud-optimized image w/ small footprint", + tags: ["base-image", "prod"], + }; + const expected = { + image: { + created_at: "2018-09-20T19:28:00Z", + description: "Cloud-optimized image w/ small footprint", + distribution: "Ubuntu", + error_message: "", + id: 38413969, + name: "ubuntu-18.04-minimal", + regions: [], + type: "custom", + tags: ["base-image", "prod"], + status: "NEW", + } + }; + const typeExpected = { + image: { + createdAt: new Date("2018-09-20T19:28:00Z"), + description: "Cloud-optimized image w/ small footprint", + distribution: "Ubuntu", + errorMessage: "", + id: 38413969, + name: "ubuntu-18.04-minimal", + regions: [], + type: "custom", + tags: ["base-image", "prod"], + status: "NEW", + } + }; + nock(baseUrl).post("/v2/images", createReqNock).reply(202, expected); + const resp = await client.v2.images.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); +}); diff --git a/tests/mocked/images.test.ts b/tests/mocked/images.test.ts new file mode 100644 index 000000000..481bfc597 --- /dev/null +++ b/tests/mocked/images.test.ts @@ -0,0 +1,418 @@ +// images.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Image_new_custom } from "../../src/dots/models/index.js"; +import { Image_update } from "../../src/dots/models/index.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Images API", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should list images", async () => { + const expected = { + images: [ + { + id: 7555620, + name: "Nifty New Snapshot", + distribution: "Ubuntu", + slug: null, + public: false, + regions: ["nyc2", "nyc3"], + created_at: "2014-11-04T22:23:02Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 7555621, + name: "Another Snapshot", + distribution: "Ubuntu", + slug: null, + public: false, + regions: ["nyc2"], + created_at: "2014-11-04T22:23:02Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 63663980, + name: "20.04 (LTS) x64", + distribution: "Ubuntu", + slug: "ubuntu-20-04-x64", + public: true, + regions: ["nyc2", "nyc3"], + created_at: "2020-05-15T05:47:50Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.36, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 7555621, + name: "A custom image", + distribution: "Arch Linux", + slug: null, + public: false, + regions: ["nyc3"], + created_at: "2014-11-04T22:23:02Z", + type: "custom", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 7555621, + name: "An APP image", + distribution: "Fedora", + slug: null, + public: false, + regions: ["nyc2", "nyc3"], + created_at: "2014-11-04T22:23:02Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + }, + { + id: 7555621, + name: "A simple tagged image", + distribution: "CentOS", + slug: null, + public: false, + regions: ["nyc2", "nyc3"], + created_at: "2014-11-04T22:23:02Z", + type: "snapshot", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: ["simple-image"], + status: "available", + error_message: "", + }, + ], + links: { pages: {} }, + meta: { total: 6 }, + }; + + const typeExpected = { + images: [ + { + id: 7555620, + name: "Nifty New Snapshot", + distribution: "Ubuntu", + slug: undefined, + public: false, + regions: ["nyc2", "nyc3"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 7555621, + name: "Another Snapshot", + distribution: "Ubuntu", + slug: undefined, + public: false, + regions: ["nyc2"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 63663980, + name: "20.04 (LTS) x64", + distribution: "Ubuntu", + slug: "ubuntu-20-04-x64", + public: true, + regions: ["nyc2", "nyc3"], + createdAt: new Date("2020-05-15T05:47:50Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.36, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 7555621, + name: "A custom image", + distribution: "Arch Linux", + slug: undefined, + public: false, + regions: ["nyc3"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "custom", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 7555621, + name: "An APP image", + distribution: "Fedora", + slug: undefined, + public: false, + regions: ["nyc2", "nyc3"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + }, + { + id: 7555621, + name: "A simple tagged image", + distribution: "CentOS", + slug: undefined, + public: false, + regions: ["nyc2", "nyc3"], + createdAt: new Date("2014-11-04T22:23:02Z"), + type: "snapshot", + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: ["simple-image"], + status: "available", + errorMessage: "", + }, + ], + links: { pages: {} }, + meta: { total: 6 }, + }; + + nock(baseUrl).get("/v2/images").reply(200, expected); + + const resp = await client.v2.images.get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should get image", async () => { + const imageId = 6918990; + const expected = { + image: { + id: 6918990, + name: "14.04 x64", + distribution: "Ubuntu", + slug: "ubuntu-16-04-x64", + public: true, + regions: [ + "nyc1", + "ams1", + "sfo1", + "nyc2", + "ams2", + "sgp1", + "lon1", + "nyc3", + "ams3", + "nyc3", + ], + created_at: "2014-10-17T20:24:33Z", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + } + }; + + const typeExpected = { + image: { + id: 6918990, + name: "14.04 x64", + distribution: "Ubuntu", + slug: "ubuntu-16-04-x64", + public: true, + regions: [ + "nyc1", + "ams1", + "sfo1", + "nyc2", + "ams2", + "sgp1", + "lon1", + "nyc3", + "ams3", + "nyc3", + ], + createdAt: new Date("2014-10-17T20:24:33Z"), + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + } + }; + + nock(baseUrl).get(`/v2/images/${imageId}`).reply(200, expected); + + const resp = await client.v2.images.byImage_id(imageId).get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should delete image", async () => { + const imageId = 6372321; + + nock(baseUrl).delete(`/v2/images/${imageId}`).reply(204); + + const resp = await client.v2.images.byImage_id(imageId).delete(); + expect(resp).toBeUndefined(); + }); + + it("should update image", async () => { + const imageId = 7938391; + const updateReq: Image_update = { + name: "Nifty New Snapshot", + distribution: "Ubuntu", + description: " ", + }; + + const updateReqNock = { + name: "Nifty New Snapshot", + distribution: "Ubuntu", + description: " ", + }; + + const expected = { + image: { + id: 7938391, + name: "new-image-name", + distribution: "Ubuntu", + slug: null, + public: false, + regions: ["nyc3", "nyc3"], + created_at: "2014-11-14T16:44:03Z", + min_disk_size: 20, + size_gigabytes: 2.34, + description: "", + tags: [], + status: "available", + error_message: "", + } + }; + + const typeExpected = { + image: { + id: 7938391, + name: "new-image-name", + distribution: "Ubuntu", + slug: undefined, + public: false, + regions: ["nyc3", "nyc3"], + createdAt: new Date("2014-11-14T16:44:03Z"), + minDiskSize: 20, + sizeGigabytes: 2.34, + description: "", + tags: [], + status: "available", + errorMessage: "", + } + }; + + nock(baseUrl).put(`/v2/images/${imageId}`, updateReqNock).reply(200, expected); + + const resp = await client.v2.images.byImage_id(imageId).put(updateReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should create custom image", async () => { + const createReq: Image_new_custom = { + name: "ubuntu-18.04-minimal", + url: "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img", + distribution: "Ubuntu", + region: "nyc3", + description: "Cloud-optimized image w/ small footprint", + tags: ["base-image", "prod"], + }; + + const createReqNock = { + name: "ubuntu-18.04-minimal", + url: "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img", + distribution: "Ubuntu", + region: "nyc3", + description: "Cloud-optimized image w/ small footprint", + tags: ["base-image", "prod"], + }; + + const expected = { + image: { + created_at: "2018-09-20T19:28:00Z", + description: "Cloud-optimized image w/ small footprint", + distribution: "Ubuntu", + error_message: "", + id: 38413969, + name: "ubuntu-18.04-minimal", + regions: [], + type: "custom", + tags: ["base-image", "prod"], + status: "NEW", + } + }; + + const typeExpected = { + image: { + createdAt: new Date("2018-09-20T19:28:00Z"), + description: "Cloud-optimized image w/ small footprint", + distribution: "Ubuntu", + errorMessage: "", + id: 38413969, + name: "ubuntu-18.04-minimal", + regions: [], + type: "custom", + tags: ["base-image", "prod"], + status: "NEW", + } + }; + + nock(baseUrl).post("/v2/images", createReqNock).reply(202, expected); + + const resp = await client.v2.images.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); +}); \ No newline at end of file diff --git a/tests/mocked/monitoring.test.js b/tests/mocked/monitoring.test.js new file mode 100644 index 000000000..688dd1808 --- /dev/null +++ b/tests/mocked/monitoring.test.js @@ -0,0 +1,314 @@ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Monitoring API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should list alert policies", async () => { + const expected = { + policies: [ + { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: 'https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ"', + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: [192018292], + tags: ["production_droplets"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + ], + links: { + first: "https//api.digitalocean.com/v2/monitoring/alerts?page=1&per_page=10", + prev: "https//api.digitalocean.com/v2/monitoring/alerts?page=2&per_page=10", + next: "https//api.digitalocean.com/v2/monitoring/alerts?page=4&per_page=10", + last: "https//api.digitalocean.com/v2/monitoring/alerts?page=5&per_page=10", + }, + meta: { total: 50 }, + }; + const typeExpected = { + policies: [ + { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: 'https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ"', + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: [192018292], + tags: ["production_droplets"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + ], + links: { + additionalData: { + first: "https//api.digitalocean.com/v2/monitoring/alerts?page=1&per_page=10", + prev: "https//api.digitalocean.com/v2/monitoring/alerts?page=2&per_page=10", + next: "https//api.digitalocean.com/v2/monitoring/alerts?page=4&per_page=10", + last: "https//api.digitalocean.com/v2/monitoring/alerts?page=5&per_page=10", + } + }, + meta: { total: 50 }, + }; + nock(baseUrl).get("/v2/monitoring/alerts").reply(200, expected); + const resp = await client.v2.monitoring.alerts.get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should create alert policy", async () => { + const createReq = { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + const createReqNock = { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + const expected = { + policy: { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + const typeExpected = { + policy: { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + nock(baseUrl).post("/v2/monitoring/alerts", createReqNock).reply(200, expected); + const resp = await client.v2.monitoring.alerts.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should get alert policy", async () => { + const alertUuid = "78b3da62-27e5-49ba-ac70-5db0b5935c64"; + const expected = { + policy: { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + const typeExpected = { + policy: { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + nock(baseUrl).get(`/v2/monitoring/alerts/${alertUuid}`).reply(200, expected); + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).get(); + expect(resp).toStrictEqual(typeExpected); + }); + it("should update alert policy", async () => { + const alertUuid = "78b3da62-27e5-49ba-ac70-5db0b5935c64"; + const updateReqNock = { + alerts: { + email: ["carl@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + const updateReq = { + alerts: { + email: ["carl@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + const expected = { + policy: { + alerts: { + email: ["carl@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + const typeExpected = { + policy: { + alerts: { + email: ["carl@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + nock(baseUrl).put(`/v2/monitoring/alerts/${alertUuid}`, updateReqNock).reply(200, expected); + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).put(updateReq); + expect(resp).toStrictEqual(typeExpected); + }); + it("should delete alert policy", async () => { + const alertUuid = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + nock(baseUrl).delete(`/v2/monitoring/alerts/${alertUuid}`).reply(204); + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).delete(); + expect(resp).toBeUndefined(); + }); +}); diff --git a/tests/mocked/monitoring.test.ts b/tests/mocked/monitoring.test.ts new file mode 100644 index 000000000..1a6e6d235 --- /dev/null +++ b/tests/mocked/monitoring.test.ts @@ -0,0 +1,340 @@ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Alert_policy_request } from "../../src/dots/models/index.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Monitoring API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should list alert policies", async () => { + const expected = { + policies: [ + { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: 'https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ"', + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: [192018292], + tags: ["production_droplets"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + ], + links: { + first: "https//api.digitalocean.com/v2/monitoring/alerts?page=1&per_page=10", + prev: "https//api.digitalocean.com/v2/monitoring/alerts?page=2&per_page=10", + next: "https//api.digitalocean.com/v2/monitoring/alerts?page=4&per_page=10", + last: "https//api.digitalocean.com/v2/monitoring/alerts?page=5&per_page=10", + }, + meta: { total: 50 }, + }; + + const typeExpected = { + policies: [ + { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: 'https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ"', + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: [192018292], + tags: ["production_droplets"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + ], + links: { + additionalData :{ + first: "https//api.digitalocean.com/v2/monitoring/alerts?page=1&per_page=10", + prev: "https//api.digitalocean.com/v2/monitoring/alerts?page=2&per_page=10", + next: "https//api.digitalocean.com/v2/monitoring/alerts?page=4&per_page=10", + last: "https//api.digitalocean.com/v2/monitoring/alerts?page=5&per_page=10", + } + }, + meta: { total: 50 }, + }; + + nock(baseUrl).get("/v2/monitoring/alerts").reply(200, expected); + + const resp = await client.v2.monitoring.alerts.get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should create alert policy", async () => { + const createReq :Alert_policy_request= { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + const createReqNock = { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + + const expected = { + policy: { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + + const typeExpected = { + policy: { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + + nock(baseUrl).post("/v2/monitoring/alerts", createReqNock).reply(200, expected); + + const resp = await client.v2.monitoring.alerts.post(createReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should get alert policy", async () => { + const alertUuid = "78b3da62-27e5-49ba-ac70-5db0b5935c64"; + + const expected = { + policy: { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + + const typeExpected = { + policy: { + alerts: { + email: ["bob@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + + nock(baseUrl).get(`/v2/monitoring/alerts/${alertUuid}`).reply(200, expected); + + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).get(); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should update alert policy", async () => { + const alertUuid = "78b3da62-27e5-49ba-ac70-5db0b5935c64"; + const updateReqNock = { + alerts: { + email: ["carl@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + + const updateReq : Alert_policy_request = { + alerts: { + email: ["carl@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + value: 80, + window: "5m", + }; + + const expected = { + policy: { + alerts: { + email: ["carl@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + + const typeExpected = { + policy: { + alerts: { + email: ["carl@example.com"], + slack: [ + { + channel: "Production Alerts", + url: "https://hooks.slack.com/services/T1234567/AAAAAAAA/ZZZZZZ", + } + ], + }, + compare: "GreaterThan", + description: "CPU Alert", + enabled: true, + entities: ["192018292"], + tags: ["droplet_tag"], + type: "v1/insights/droplet/cpu", + uuid: "78b3da62-27e5-49ba-ac70-5db0b5935c64", + value: 80, + window: "5m", + } + }; + + nock(baseUrl).put(`/v2/monitoring/alerts/${alertUuid}`, updateReqNock).reply(200, expected); + + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).put(updateReq); + expect(resp).toStrictEqual(typeExpected); + }); + + it("should delete alert policy", async () => { + const alertUuid = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + + nock(baseUrl).delete(`/v2/monitoring/alerts/${alertUuid}`).reply(204); + + const resp = await client.v2.monitoring.alerts.byAlert_uuid(alertUuid).delete(); + expect(resp).toBeUndefined(); + }); + +}); \ No newline at end of file diff --git a/tests/mocked/oneClicks.test.js b/tests/mocked/oneClicks.test.js new file mode 100644 index 000000000..67a3c33f6 --- /dev/null +++ b/tests/mocked/oneClicks.test.js @@ -0,0 +1,153 @@ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("One-click API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should list one-click applications", async () => { + const expected = { + "1_clicks": [ + { "slug": "monitoring", "type": "kubernetes" }, + { "slug": "wordpress-18-04", "type": "droplet" }, + ] + }; + const typeExpected = { + oneClicks: [ + { slug: "monitoring", type: "kubernetes" }, + { slug: "wordpress-18-04", type: "droplet" }, + ] + }; + nock(baseUrl).get("/v2/1-clicks").reply(200, expected); + const oneClickApps = await client.v2.oneClicks.get(); + expect(oneClickApps).toStrictEqual(typeExpected); + }); + it("should list one-click applications with query", async () => { + const expected = { + "1_clicks": [ + { "slug": "wordpress-18-04", "type": "droplet" }, + ] + }; + const typeExpected = { + oneClicks: [ + { slug: "wordpress-18-04", type: "droplet" }, + ] + }; + nock(baseUrl) + .get("/v2/1-clicks") + .query({ type: "droplet" }) + .reply(200, expected); + const oneClickApps = await client.v2.oneClicks.get({ + queryParameters: { + type: "droplet" + } + }); + expect(oneClickApps).toStrictEqual(typeExpected); + }); + it("should install kubernetes one-click application", async () => { + const installReqNock = { + addon_slugs: ["monitoring", "kube-state-metrics"], + cluster_uuid: "bd5f5959-5e1e-4205-a714-a914373942af" + }; + const installReq = { + addonSlugs: ["monitoring", "kube-state-metrics"], + clusterUuid: "bd5f5959-5e1e-4205-a714-a914373942af" + }; + const expected = { + message: "Successfully kicked off addon job." + }; + const typeExpected = { + message: "Successfully kicked off addon job." + }; + nock(baseUrl) + .post("/v2/1-clicks/kubernetes", installReqNock) + .reply(200, expected); + const installResp = await client.v2.oneClicks.kubernetes.post(installReq); + expect(installResp).toStrictEqual(typeExpected); + }); + it("should install kubernetes one-click application with proper request body", async () => { + const installReqNock = { + addon_slugs: ["monitoring", "kube-state-metrics"], + cluster_uuid: "bd5f5959-5e1e-4205-a714-a914373942af" + }; + const installReq = { + addonSlugs: ["monitoring", "kube-state-metrics"], + clusterUuid: "bd5f5959-5e1e-4205-a714-a914373942af" + }; + const expected = { + message: "Successfully kicked off addon job." + }; + const typeExpected = { + message: "Successfully kicked off addon job." + }; + nock(baseUrl) + .post("/v2/1-clicks/kubernetes", installReqNock) + .reply(200, expected); + const installResp = await client.v2.oneClicks.kubernetes.post(installReq); + expect(installResp).toStrictEqual(typeExpected); + }); + it("should handle one-click list with different types", async () => { + const kubernetesExpected = { + "1_clicks": [ + { "slug": "monitoring", "type": "kubernetes" }, + { "slug": "prometheus", "type": "kubernetes" }, + ] + }; + const kubernetesTypeExpected = { + oneClicks: [ + { slug: "monitoring", type: "kubernetes" }, + { slug: "prometheus", type: "kubernetes" }, + ] + }; + nock(baseUrl) + .get("/v2/1-clicks") + .query({ type: "kubernetes" }) + .reply(200, kubernetesExpected); + const kubernetesApps = await client.v2.oneClicks.get({ + queryParameters: { + type: "kubernetes" + } + }); + expect(kubernetesApps).toStrictEqual(kubernetesTypeExpected); + }); + it("should handle empty one-click list", async () => { + const expected = { + "1_clicks": [] + }; + const typeExpected = { + oneClicks: [] + }; + nock(baseUrl) + .get("/v2/1-clicks") + .reply(200, expected); + const oneClickApps = await client.v2.oneClicks.get(); + expect(oneClickApps).toStrictEqual(typeExpected); + }); + it("should handle one-click install with minimal request", async () => { + const minimalReq = { + addonSlugs: ["monitoring"], + clusterUuid: "test-cluster-uuid" + }; + const minimalReqNock = { + addon_slugs: ["monitoring"], + cluster_uuid: "test-cluster-uuid" + }; + const expected = { + message: "Successfully kicked off addon job." + }; + const typeExpected = { + message: "Successfully kicked off addon job." + }; + nock(baseUrl) + .post("/v2/1-clicks/kubernetes", minimalReqNock) + .reply(200, expected); + const installResp = await client.v2.oneClicks.kubernetes.post(minimalReq); + expect(installResp).toStrictEqual(typeExpected); + }); +}); diff --git a/tests/mocked/oneClicks.test.ts b/tests/mocked/oneClicks.test.ts new file mode 100644 index 000000000..703e64f58 --- /dev/null +++ b/tests/mocked/oneClicks.test.ts @@ -0,0 +1,192 @@ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { OneClicks_create } from "../../src/dots/models/index.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("One-click API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should list one-click applications", async () => { + const expected = { + "1_clicks": [ + { "slug": "monitoring", "type": "kubernetes" }, + { "slug": "wordpress-18-04", "type": "droplet" }, + ] + }; + + const typeExpected = { + oneClicks: [ + { slug: "monitoring", type: "kubernetes" }, + { slug: "wordpress-18-04", type: "droplet" }, + ] + }; + + nock(baseUrl).get("/v2/1-clicks").reply(200, expected); + + const oneClickApps = await client.v2.oneClicks.get(); + expect(oneClickApps).toStrictEqual(typeExpected); + }); + + it("should list one-click applications with query", async () => { + const expected = { + "1_clicks": [ + { "slug": "wordpress-18-04", "type": "droplet" }, + ] + }; + + const typeExpected = { + oneClicks: [ + { slug: "wordpress-18-04", type: "droplet" }, + ] + }; + + nock(baseUrl) + .get("/v2/1-clicks") + .query({ type: "droplet" }) + .reply(200, expected); + + const oneClickApps = await client.v2.oneClicks.get({ + queryParameters: { + type: "droplet" + } + }); + + expect(oneClickApps).toStrictEqual(typeExpected); + }); + + it("should install kubernetes one-click application", async () => { + const installReqNock = { + addon_slugs: ["monitoring", "kube-state-metrics"], + cluster_uuid: "bd5f5959-5e1e-4205-a714-a914373942af" + }; + + const installReq : OneClicks_create = { + addonSlugs: ["monitoring", "kube-state-metrics"], + clusterUuid: "bd5f5959-5e1e-4205-a714-a914373942af" + }; + + const expected = { + message: "Successfully kicked off addon job." + }; + + const typeExpected = { + message: "Successfully kicked off addon job." + }; + + nock(baseUrl) + .post("/v2/1-clicks/kubernetes", installReqNock) + .reply(200, expected); + + const installResp = await client.v2.oneClicks.kubernetes.post(installReq); + expect(installResp).toStrictEqual(typeExpected); + }); + + it("should install kubernetes one-click application with proper request body", async () => { + const installReqNock = { + addon_slugs: ["monitoring", "kube-state-metrics"], + cluster_uuid: "bd5f5959-5e1e-4205-a714-a914373942af" + }; + + const installReq :OneClicks_create= { + addonSlugs: ["monitoring", "kube-state-metrics"], + clusterUuid: "bd5f5959-5e1e-4205-a714-a914373942af" + }; + + const expected = { + message: "Successfully kicked off addon job." + }; + + const typeExpected = { + message: "Successfully kicked off addon job." + }; + + nock(baseUrl) + .post("/v2/1-clicks/kubernetes", installReqNock) + .reply(200, expected); + + const installResp = await client.v2.oneClicks.kubernetes.post(installReq); + expect(installResp).toStrictEqual(typeExpected); + }); + + it("should handle one-click list with different types", async () => { + const kubernetesExpected = { + "1_clicks": [ + { "slug": "monitoring", "type": "kubernetes" }, + { "slug": "prometheus", "type": "kubernetes" }, + ] + }; + + const kubernetesTypeExpected = { + oneClicks: [ + { slug: "monitoring", type: "kubernetes" }, + { slug: "prometheus", type: "kubernetes" }, + ] + }; + + nock(baseUrl) + .get("/v2/1-clicks") + .query({ type: "kubernetes" }) + .reply(200, kubernetesExpected); + + const kubernetesApps = await client.v2.oneClicks.get({ + queryParameters: { + type: "kubernetes" + } + }); + + expect(kubernetesApps).toStrictEqual(kubernetesTypeExpected); + }); + + it("should handle empty one-click list", async () => { + const expected = { + "1_clicks": [] + }; + + const typeExpected = { + oneClicks: [] + }; + + nock(baseUrl) + .get("/v2/1-clicks") + .reply(200, expected); + + const oneClickApps = await client.v2.oneClicks.get(); + + expect(oneClickApps).toStrictEqual(typeExpected); + }); + + it("should handle one-click install with minimal request", async () => { + const minimalReq :OneClicks_create = { + addonSlugs: ["monitoring"], + clusterUuid: "test-cluster-uuid" + }; + + const minimalReqNock = { + addon_slugs: ["monitoring"], + cluster_uuid: "test-cluster-uuid" + }; + + const expected = { + message: "Successfully kicked off addon job." + }; + + const typeExpected = { + message: "Successfully kicked off addon job." + }; + + nock(baseUrl) + .post("/v2/1-clicks/kubernetes", minimalReqNock) + .reply(200, expected); + + const installResp = await client.v2.oneClicks.kubernetes.post(minimalReq); + expect(installResp).toStrictEqual(typeExpected); + }); +}); \ No newline at end of file diff --git a/tests/mocked/projects.test.js b/tests/mocked/projects.test.js new file mode 100644 index 000000000..6810bd635 --- /dev/null +++ b/tests/mocked/projects.test.js @@ -0,0 +1,481 @@ +/** + * Mock tests for the Projects + */ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Projects API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should list projects", async () => { + const expected = { + "projects": [ + { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "is_default": false, + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + }, + { + "id": "addb4547-6bab-419a-8542-76263a033cf6", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "Default", + "description": "Default project", + "purpose": "Just trying out DigitalOcean", + "environment": "Development", + "is_default": true, + "created_at": "2017-10-19T21:44:20Z", + "updated_at": "2019-11-05T18:50:03Z", + }, + ], + "links": { + "pages": { + "first": "https://api.digitalocean.com/v2/projects?page=1", + "last": "https://api.digitalocean.com/v2/projects?page=1", + } + }, + "meta": { "total": 2 }, + }; + const typeExpected = { + projects: [ + { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + isDefault: false, + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + }, + { + id: "addb4547-6bab-419a-8542-76263a033cf6", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "Default", + description: "Default project", + purpose: "Just trying out DigitalOcean", + environment: "Development", + isDefault: true, + createdAt: new Date("2017-10-19T21:44:20Z"), + updatedAt: new Date("2019-11-05T18:50:03Z"), + }, + ], + links: { + pages: { + first: "https://api.digitalocean.com/v2/projects?page=1", + additionalData: { + last: "https://api.digitalocean.com/v2/projects?page=1", + } + } + }, + meta: { total: 2 }, + }; + nock(baseUrl).get("/v2/projects").reply(200, expected); + const listResp = await client.v2.projects.get(); + expect(listResp).toStrictEqual(typeExpected); + }); + it("should list projects with pagination", async () => { + const expected = { + "projects": [ + { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "is_default": false, + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + }, + { + "id": "addb4547-6bab-419a-8542-76263a033cf6", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "Default", + "description": "Default project", + "purpose": "Just trying out DigitalOcean", + "environment": "Development", + "is_default": true, + "created_at": "2017-10-19T21:44:20Z", + "updated_at": "2019-11-05T18:50:03Z", + }, + ], + "links": { + "pages": { + "first": "https://api.digitalocean.com/v2/projects?page=2&per_page=20", + "last": "https://api.digitalocean.com/v2/projects?page=6&per_page=20", + } + }, + "meta": { "total": 6 }, + }; + const typeExpected = { + projects: [ + { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + isDefault: false, + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + }, + { + id: "addb4547-6bab-419a-8542-76263a033cf6", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "Default", + description: "Default project", + purpose: "Just trying out DigitalOcean", + environment: "Development", + isDefault: true, + createdAt: new Date("2017-10-19T21:44:20Z"), + updatedAt: new Date("2019-11-05T18:50:03Z"), + }, + ], + links: { + pages: { + first: "https://api.digitalocean.com/v2/projects?page=2&per_page=20", + additionalData: { + last: "https://api.digitalocean.com/v2/projects?page=6&per_page=20", + } + } + }, + meta: { total: 6 }, + }; + nock(baseUrl) + .get("/v2/projects") + .query({ per_page: 20, page: 1 }) + .reply(200, expected); + const listResp = await client.v2.projects.get({ + queryParameters: { + perPage: 20, + page: 1 + } + }); + expect(listResp).toStrictEqual(typeExpected); + }); + it("should create a project", async () => { + const createReq = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + }; + const createReqNock = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + }; + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + nock(baseUrl) + .post("/v2/projects", createReqNock) + .reply(201, expected); + const createResp = await client.v2.projects.post(createReq); + expect(createResp).toStrictEqual(typeExpected); + }); + it("should get a project by ID", async () => { + const projectId = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + nock(baseUrl) + .get(`/v2/projects/${projectId}`) + .reply(200, expected); + const getResp = await client.v2.projects.byProject_id(projectId).get(); + expect(getResp).toStrictEqual(typeExpected); + }); + it("should delete a project", async () => { + const projectId = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + nock(baseUrl) + .delete(`/v2/projects/${projectId}`) + .reply(204); + const delResp = await client.v2.projects.byProject_id(projectId).delete(); + expect(delResp).toBeUndefined(); + }); + it("should update a project", async () => { + const projectId = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + const updateReq = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + isDefault: false, + }; + const updateReqNock = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + is_default: false, + }; + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + nock(baseUrl) + .put(`/v2/projects/${projectId}`, updateReqNock) + .reply(200, expected); + const updateResp = await client.v2.projects.byProject_id(projectId).put(updateReq); + expect(updateResp).toStrictEqual(typeExpected); + }); + it("should patch a project", async () => { + const projectId = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + const patchReq = { + name: "my-web-api", + }; + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + nock(baseUrl) + .patch(`/v2/projects/${projectId}`, patchReq) + .reply(200, expected); + const patchResp = await client.v2.projects.byProject_id(projectId).patch(patchReq); + expect(patchResp).toStrictEqual(typeExpected); + }); + it("should get the default project", async () => { + const expected = { + "project": { + "id": "addb4547-6bab-419a-8542-76263a033cf6", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "Default", + "description": "Default project", + "purpose": "Just trying out DigitalOcean", + "environment": "Development", + "is_default": true, + "created_at": "2017-10-19T21:44:20Z", + "updated_at": "2019-11-05T18:50:03Z", + } + }; + const typeExpected = { + project: { + id: "addb4547-6bab-419a-8542-76263a033cf6", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "Default", + description: "Default project", + purpose: "Just trying out DigitalOcean", + environment: "Development", + isDefault: true, + createdAt: new Date("2017-10-19T21:44:20Z"), + updatedAt: new Date("2019-11-05T18:50:03Z"), + } + }; + nock(baseUrl) + .get("/v2/projects/default") + .reply(200, expected); + const getResp = await client.v2.projects.defaultEscaped.get(); + expect(getResp).toStrictEqual(typeExpected); + }); + it("should update the default project", async () => { + const UpdateReq = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + isDefault: false, + }; + const UpdateReqNock = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + is_default: false, + }; + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + nock(baseUrl) + .put("/v2/projects/default", UpdateReqNock) + .reply(200, expected); + const updateResp = await client.v2.projects.defaultEscaped.put(UpdateReq); + expect(updateResp).toStrictEqual(typeExpected); + }); + it("should patch the default project", async () => { + const patchReq = { + name: "my-web-api", + }; + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + nock(baseUrl) + .patch("/v2/projects/default", patchReq) + .reply(200, expected); + const updateResp = await client.v2.projects.defaultEscaped.patch(patchReq); + expect(updateResp).toStrictEqual(typeExpected); + }); +}); diff --git a/tests/mocked/projects.test.ts b/tests/mocked/projects.test.ts new file mode 100644 index 000000000..63e60206e --- /dev/null +++ b/tests/mocked/projects.test.ts @@ -0,0 +1,531 @@ +/** + * Mock tests for the Projects + */ + +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Project_base, Project } from "../../src/dots/models/index.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Projects API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should list projects", async () => { + const expected = { + "projects": [ + { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "is_default": false, + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + }, + { + "id": "addb4547-6bab-419a-8542-76263a033cf6", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "Default", + "description": "Default project", + "purpose": "Just trying out DigitalOcean", + "environment": "Development", + "is_default": true, + "created_at": "2017-10-19T21:44:20Z", + "updated_at": "2019-11-05T18:50:03Z", + }, + ], + "links": { + "pages": { + "first": "https://api.digitalocean.com/v2/projects?page=1", + "last": "https://api.digitalocean.com/v2/projects?page=1", + } + }, + "meta": {"total": 2}, + }; + + const typeExpected = { + projects: [ + { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + isDefault: false, + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + }, + { + id: "addb4547-6bab-419a-8542-76263a033cf6", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "Default", + description: "Default project", + purpose: "Just trying out DigitalOcean", + environment: "Development", + isDefault: true, + createdAt: new Date("2017-10-19T21:44:20Z"), + updatedAt: new Date("2019-11-05T18:50:03Z"), + }, + ], + links: { + pages: { + first: "https://api.digitalocean.com/v2/projects?page=1", + additionalData: { + last: "https://api.digitalocean.com/v2/projects?page=1", + } + } + }, + meta: { total: 2 }, + }; + + nock(baseUrl).get("/v2/projects").reply(200, expected); + + const listResp = await client.v2.projects.get(); + expect(listResp).toStrictEqual(typeExpected); + }); + + it("should list projects with pagination", async () => { + const expected = { + "projects": [ + { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "is_default": false, + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + }, + { + "id": "addb4547-6bab-419a-8542-76263a033cf6", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "Default", + "description": "Default project", + "purpose": "Just trying out DigitalOcean", + "environment": "Development", + "is_default": true, + "created_at": "2017-10-19T21:44:20Z", + "updated_at": "2019-11-05T18:50:03Z", + }, + ], + "links": { + "pages": { + "first": "https://api.digitalocean.com/v2/projects?page=2&per_page=20", + "last": "https://api.digitalocean.com/v2/projects?page=6&per_page=20", + } + }, + "meta": {"total": 6}, + }; + + const typeExpected = { + projects: [ + { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + isDefault: false, + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + }, + { + id: "addb4547-6bab-419a-8542-76263a033cf6", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "Default", + description: "Default project", + purpose: "Just trying out DigitalOcean", + environment: "Development", + isDefault: true, + createdAt: new Date("2017-10-19T21:44:20Z"), + updatedAt: new Date("2019-11-05T18:50:03Z"), + }, + ], + links: { + pages: { + first: "https://api.digitalocean.com/v2/projects?page=2&per_page=20", + additionalData: { + last: "https://api.digitalocean.com/v2/projects?page=6&per_page=20", + } + } + }, + meta: { total: 6 }, + }; + + nock(baseUrl) + .get("/v2/projects") + .query({ per_page: 20, page: 1 }) + .reply(200, expected); + + const listResp = await client.v2.projects.get({ + queryParameters: { + perPage: 20, + page: 1 + } + }); + expect(listResp).toStrictEqual(typeExpected); + }); + + it("should create a project", async () => { + const createReq : Project_base= { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + }; + + const createReqNock= { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + }; + + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + + nock(baseUrl) + .post("/v2/projects", createReqNock) + .reply(201, expected); + + const createResp = await client.v2.projects.post(createReq); + expect(createResp).toStrictEqual(typeExpected); + }); + + it("should get a project by ID", async () => { + const projectId = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + + nock(baseUrl) + .get(`/v2/projects/${projectId}`) + .reply(200, expected); + + const getResp = await client.v2.projects.byProject_id(projectId).get(); + expect(getResp).toStrictEqual(typeExpected); + }); + + it("should delete a project", async () => { + const projectId = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + + nock(baseUrl) + .delete(`/v2/projects/${projectId}`) + .reply(204); + + const delResp = await client.v2.projects.byProject_id(projectId).delete(); + expect(delResp).toBeUndefined(); + }); + + it("should update a project", async () => { + const projectId = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + const updateReq : Project = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + isDefault: false, + }; + + const updateReqNock = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + is_default: false, + }; + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + + nock(baseUrl) + .put(`/v2/projects/${projectId}`, updateReqNock) + .reply(200, expected); + + const updateResp = await client.v2.projects.byProject_id(projectId).put(updateReq); + expect(updateResp).toStrictEqual(typeExpected); + }); + + it("should patch a project", async () => { + const projectId = "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679"; + const patchReq = { + name: "my-web-api", + }; + + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + + nock(baseUrl) + .patch(`/v2/projects/${projectId}`, patchReq) + .reply(200, expected); + + const patchResp = await client.v2.projects.byProject_id(projectId).patch(patchReq); + expect(patchResp).toStrictEqual(typeExpected); + }); + + it("should get the default project", async () => { + const expected = { + "project": { + "id": "addb4547-6bab-419a-8542-76263a033cf6", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "Default", + "description": "Default project", + "purpose": "Just trying out DigitalOcean", + "environment": "Development", + "is_default": true, + "created_at": "2017-10-19T21:44:20Z", + "updated_at": "2019-11-05T18:50:03Z", + } + }; + + const typeExpected = { + project: { + id: "addb4547-6bab-419a-8542-76263a033cf6", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "Default", + description: "Default project", + purpose: "Just trying out DigitalOcean", + environment: "Development", + isDefault: true, + createdAt: new Date("2017-10-19T21:44:20Z"), + updatedAt: new Date("2019-11-05T18:50:03Z"), + } + }; + + nock(baseUrl) + .get("/v2/projects/default") + .reply(200, expected); + + const getResp = await client.v2.projects.defaultEscaped.get(); + expect(getResp).toStrictEqual(typeExpected); + }); + + it("should update the default project", async () => { + const UpdateReq : Project = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + isDefault: false, + }; + + const UpdateReqNock = { + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + is_default: false, + }; + + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + + nock(baseUrl) + .put("/v2/projects/default", UpdateReqNock) + .reply(200, expected); + + const updateResp = await client.v2.projects.defaultEscaped.put(UpdateReq); + expect(updateResp).toStrictEqual(typeExpected); + }); + + it("should patch the default project", async () => { + const patchReq = { + name: "my-web-api", + }; + + const expected = { + "project": { + "id": "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + "owner_uuid": "99525febec065ca37b2ffe4f852fd2b2581895e7", + "owner_id": 258992, + "name": "my-web-api", + "description": "My website API", + "purpose": "Service or API", + "environment": "Production", + "created_at": "2018-09-27T20:10:35Z", + "updated_at": "2018-09-27T20:10:35Z", + "is_default": false, + } + }; + + const typeExpected = { + project: { + id: "4e1bfbc3-dc3e-41f2-a18f-1b4d7ba71679", + ownerUuid: "99525febec065ca37b2ffe4f852fd2b2581895e7", + ownerId: 258992, + name: "my-web-api", + description: "My website API", + purpose: "Service or API", + environment: "Production", + createdAt: new Date("2018-09-27T20:10:35Z"), + updatedAt: new Date("2018-09-27T20:10:35Z"), + isDefault: false, + } + }; + + nock(baseUrl) + .patch("/v2/projects/default", patchReq) + .reply(200, expected); + + const updateResp = await client.v2.projects.defaultEscaped.patch(patchReq); + expect(updateResp).toStrictEqual(typeExpected); + }); + +}); \ No newline at end of file diff --git a/tests/mocked/regions.test.js b/tests/mocked/regions.test.js new file mode 100644 index 000000000..b7cf6231f --- /dev/null +++ b/tests/mocked/regions.test.js @@ -0,0 +1,105 @@ +// cdn.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("CDN API", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should Mock the regions list operation", async () => { + const expected = { + regions: [ + { + name: "New York 3", + slug: "nyc3", + features: [ + "private_networking", + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: [ + "s-1vcpu-1gb", + "s-1vcpu-2gb", + "s-1vcpu-3gb", + "s-2vcpu-2gb", + "s-3vcpu-1gb", + "s-2vcpu-4gb", + "s-4vcpu-8gb", + "s-6vcpu-16gb", + "s-8vcpu-32gb", + "s-12vcpu-48gb", + "s-16vcpu-64gb", + "s-20vcpu-96gb", + "s-24vcpu-128gb", + "s-32vcpu-192g", + ], + }, + ], + links: { + pages: { + last: "https://api.digitalocean.com/v2/regions?page=13&per_page=1", + next: "https://api.digitalocean.com/v2/regions?page=2&per_page=1", + }, + }, + meta: { total: 1 }, + }; + const typeExpected = { + regions: [ + { + name: "New York 3", + slug: "nyc3", + features: [ + "private_networking", + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: [ + "s-1vcpu-1gb", + "s-1vcpu-2gb", + "s-1vcpu-3gb", + "s-2vcpu-2gb", + "s-3vcpu-1gb", + "s-2vcpu-4gb", + "s-4vcpu-8gb", + "s-6vcpu-16gb", + "s-8vcpu-32gb", + "s-12vcpu-48gb", + "s-16vcpu-64gb", + "s-20vcpu-96gb", + "s-24vcpu-128gb", + "s-32vcpu-192g", + ], + }, + ], + links: { + pages: { + additionalData: { + last: "https://api.digitalocean.com/v2/regions?page=13&per_page=1", + next: "https://api.digitalocean.com/v2/regions?page=2&per_page=1", + } + }, + }, + meta: { total: 1 }, + }; + nock(baseUrl).get("/v2/regions") + .reply(200, expected); + const resp = await client.v2.regions.get(); + expect(resp).toStrictEqual(typeExpected); + }); +}); diff --git a/tests/mocked/regions.test.ts b/tests/mocked/regions.test.ts new file mode 100644 index 000000000..d3a53c7d8 --- /dev/null +++ b/tests/mocked/regions.test.ts @@ -0,0 +1,110 @@ +// cdn.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("CDN API", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should Mock the regions list operation", async () => { + const expected = { + regions: [ + { + name: "New York 3", + slug: "nyc3", + features: [ + "private_networking", + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: [ + "s-1vcpu-1gb", + "s-1vcpu-2gb", + "s-1vcpu-3gb", + "s-2vcpu-2gb", + "s-3vcpu-1gb", + "s-2vcpu-4gb", + "s-4vcpu-8gb", + "s-6vcpu-16gb", + "s-8vcpu-32gb", + "s-12vcpu-48gb", + "s-16vcpu-64gb", + "s-20vcpu-96gb", + "s-24vcpu-128gb", + "s-32vcpu-192g", + ], + }, + ], + links: { + pages: { + last: "https://api.digitalocean.com/v2/regions?page=13&per_page=1", + next: "https://api.digitalocean.com/v2/regions?page=2&per_page=1", + }, + }, + meta: { total: 1 }, + }; + const typeExpected = { + regions: [ + { + name: "New York 3", + slug: "nyc3", + features: [ + "private_networking", + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: [ + "s-1vcpu-1gb", + "s-1vcpu-2gb", + "s-1vcpu-3gb", + "s-2vcpu-2gb", + "s-3vcpu-1gb", + "s-2vcpu-4gb", + "s-4vcpu-8gb", + "s-6vcpu-16gb", + "s-8vcpu-32gb", + "s-12vcpu-48gb", + "s-16vcpu-64gb", + "s-20vcpu-96gb", + "s-24vcpu-128gb", + "s-32vcpu-192g", + ], + }, + ], + links: { + pages: { + additionalData: { + last: "https://api.digitalocean.com/v2/regions?page=13&per_page=1", + next: "https://api.digitalocean.com/v2/regions?page=2&per_page=1", + } + }, + }, + meta: { total: 1 }, + }; + + nock(baseUrl).get("/v2/regions") + .reply(200, expected); + const resp = await client.v2.regions.get(); + expect(resp).toStrictEqual(typeExpected); + }); + +}); \ No newline at end of file diff --git a/tests/mocked/reservedIps.test.js b/tests/mocked/reservedIps.test.js new file mode 100644 index 000000000..122d03794 --- /dev/null +++ b/tests/mocked/reservedIps.test.js @@ -0,0 +1,348 @@ +// cdn.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("CDN API", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should Mock the reserved IPs create operation.", async () => { + const expected = { + reserved_ip: { + ip: "192.0.2.1", + droplet: null, + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + locked: false, + }, + }; + const typeExpected = { + reservedIp: { + ip: "192.0.2.1", + droplet: {}, + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + locked: false, + }, + }; + const createReqNock = { + dropletId: 2457247, + }; + const createReq = { + "droplet_id": 2457247, + }; + nock(baseUrl).post("/v2/reserved_ips", createReq).reply(201, expected); + const resp = await client.v2.reserved_ips.post(createReqNock); + expect(resp).toEqual(typeExpected); + }); + it("Should Mock the reserved IPs delete operation.", async () => { + nock(baseUrl).delete("/v2/reserved_ips/192.0.2.1").reply(204); + const resp = await client.v2.reserved_ips.byReserved_ip("192.0.2.1").delete(); + expect(resp).toBeUndefined(); + }); + it("Should Mock the reserved IPs actions list operation", async () => { + const expected = { + actions: [ + { + id: 1492489780, + status: "completed", + type: "unassign_ip", + started_at: "2022-05-23T21:03:21Z", + completed_at: "2022-05-23T21:03:22Z", + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + { + id: 1453215653, + status: "completed", + type: "assign_ip", + started_at: "2022-04-04T15:54:43Z", + completed_at: "2022-04-04T15:54:46Z", + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + { + id: 1453215650, + status: "completed", + type: "reserve_ip", + started_at: "2022-04-04T15:54:43Z", + completed_at: "2022-04-04T15:54:43Z", + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + ], + links: {}, + meta: { total: 3 }, + }; + const typeExpected = { + actions: [ + { + id: 1492489780, + status: "completed", + type: "unassign_ip", + startedAt: new Date("2022-05-23T21:03:21Z"), + completedAt: new Date("2022-05-23T21:03:22Z"), + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + { + id: 1453215653, + status: "completed", + type: "assign_ip", + startedAt: new Date("2022-04-04T15:54:43Z"), + completedAt: new Date("2022-04-04T15:54:46Z"), + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + { + id: 1453215650, + status: "completed", + type: "reserve_ip", + startedAt: new Date("2022-04-04T15:54:43Z"), + completedAt: new Date("2022-04-04T15:54:43Z"), + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + ], + links: {}, + meta: { total: 3 }, + }; + nock(baseUrl).get("/v2/reserved_ips/192.0.2.1/actions").reply(200, expected); + const resp = await client.v2.reserved_ips.byReserved_ip("192.0.2.1").actions.get(); + expect(resp).toEqual(typeExpected); + }); + it("Should Mock the reserved IPs actions get operation", async () => { + const expected = { + action: { + id: 1492489780, + status: "completed", + type: "unassign_ip", + started_at: "2022-05-23T21:03:21Z", + completed_at: "2022-05-23T21:03:22Z", + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + }; + const typeExpected = { + action: { + id: 1492489780, + status: "completed", + type: "unassign_ip", + startedAt: new Date("2022-05-23T21:03:21Z"), + completedAt: new Date("2022-05-23T21:03:22Z"), + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + }; + nock(baseUrl).get("/v2/reserved_ips/192.0.2.1/actions/1492489780").reply(200, expected); + const resp = await client.v2.reserved_ips.byReserved_ip("192.0.2.1").actions.byAction_id(1492489780).get(); + expect(resp).toEqual(typeExpected); + }); + it("Should Mock the reserved IPs actions post operation..", async () => { + const expected = { + action: { + id: 1492489780, + status: "in-progress", + type: "unassign_ip", + started_at: "2022-05-23T21:03:21Z", + completed_at: NaN, + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + }; + const typeExpected = { + action: { + id: 1492489780, + status: "in-progress", + type: "unassign_ip", + startedAt: new Date("2022-05-23T21:03:21Z"), + completedAt: undefined, + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + }; + nock(baseUrl).post("/v2/reserved_ips/192.0.2.1/actions", { "type": "unassign" }).reply(200, expected); + const resp = await client.v2.reserved_ips.byReserved_ip("192.0.2.1").actions.post({ "type": "unassign" }); + expect(resp).toEqual(typeExpected); + }); +}); diff --git a/tests/mocked/reservedIps.test.ts b/tests/mocked/reservedIps.test.ts new file mode 100644 index 000000000..13dd5734d --- /dev/null +++ b/tests/mocked/reservedIps.test.ts @@ -0,0 +1,369 @@ +// cdn.test.ts +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("CDN API", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should Mock the reserved IPs create operation.", async () => { + const expected = { + reserved_ip: { + ip: "192.0.2.1", + droplet: null, + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + locked: false, + }, + }; + + const typeExpected = { + reservedIp: { + ip: "192.0.2.1", + droplet: {}, + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + locked: false, + }, + }; + const createReqNock={ + dropletId: 2457247, + }; + const createReq = { + "droplet_id": 2457247, + }; + nock(baseUrl).post("/v2/reserved_ips", createReq).reply(201, expected); + const resp = await client.v2.reserved_ips.post(createReqNock); + expect(resp).toEqual(typeExpected); + }); + + it("Should Mock the reserved IPs delete operation.", async () => { + nock(baseUrl).delete("/v2/reserved_ips/192.0.2.1").reply(204); + const resp = await client.v2.reserved_ips.byReserved_ip("192.0.2.1").delete(); + expect(resp).toBeUndefined(); + }); + + it("Should Mock the reserved IPs actions list operation", async () => { + const expected = { + actions: [ + { + id: 1492489780, + status: "completed", + type: "unassign_ip", + started_at: "2022-05-23T21:03:21Z", + completed_at: "2022-05-23T21:03:22Z", + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + { + id: 1453215653, + status: "completed", + type: "assign_ip", + started_at: "2022-04-04T15:54:43Z", + completed_at: "2022-04-04T15:54:46Z", + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + { + id: 1453215650, + status: "completed", + type: "reserve_ip", + started_at: "2022-04-04T15:54:43Z", + completed_at: "2022-04-04T15:54:43Z", + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + ], + links: {}, + meta: { total: 3 }, + }; + + const typeExpected = { + actions: [ + { + id: 1492489780, + status: "completed", + type: "unassign_ip", + startedAt: new Date("2022-05-23T21:03:21Z"), + completedAt: new Date("2022-05-23T21:03:22Z"), + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + { + id: 1453215653, + status: "completed", + type: "assign_ip", + startedAt: new Date("2022-04-04T15:54:43Z"), + completedAt: new Date("2022-04-04T15:54:46Z"), + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + { + id: 1453215650, + status: "completed", + type: "reserve_ip", + startedAt: new Date("2022-04-04T15:54:43Z"), + completedAt: new Date("2022-04-04T15:54:43Z"), + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + ], + links: {}, + meta: { total: 3 }, + }; + + nock(baseUrl).get("/v2/reserved_ips/192.0.2.1/actions").reply(200, expected); + const resp = await client.v2.reserved_ips.byReserved_ip("192.0.2.1").actions.get(); + expect(resp).toEqual(typeExpected); + }); + + it("Should Mock the reserved IPs actions get operation", async () => { + + const expected = { + action: { + id: 1492489780, + status: "completed", + type: "unassign_ip", + started_at: "2022-05-23T21:03:21Z", + completed_at: "2022-05-23T21:03:22Z", + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + }; + + const typeExpected = { + action: { + id: 1492489780, + status: "completed", + type: "unassign_ip", + startedAt: new Date("2022-05-23T21:03:21Z"), + completedAt: new Date("2022-05-23T21:03:22Z"), + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + }; + + nock(baseUrl).get("/v2/reserved_ips/192.0.2.1/actions/1492489780").reply(200, expected); + const resp = await client.v2.reserved_ips.byReserved_ip("192.0.2.1").actions.byAction_id(1492489780).get(); + expect(resp).toEqual(typeExpected); + }); + + + it("Should Mock the reserved IPs actions post operation..", async () => { + + const expected = { + action: { + id: 1492489780, + status: "in-progress", + type: "unassign_ip", + started_at: "2022-05-23T21:03:21Z", + completed_at: NaN, + resource_id: 2680918192, + resource_type: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + region_slug: "nyc3", + }, + }; + + const typeExpected = { + action: { + id: 1492489780, + status: "in-progress", + type: "unassign_ip", + startedAt: new Date("2022-05-23T21:03:21Z"), + completedAt: undefined, + resourceId: 2680918192, + resourceType: "reserved_ip", + region: { + name: "New York 3", + slug: "nyc3", + features: [ + "backups", + "ipv6", + "metadata", + "install_agent", + "storage", + "image_transfer", + ], + available: true, + sizes: ["s-1vcpu-1gb", "s-1vcpu-1gb-amd", "s-1vcpu-1gb-intel"], + }, + regionSlug: "nyc3", + }, + }; + + nock(baseUrl).post("/v2/reserved_ips/192.0.2.1/actions", {"type": "unassign"}).reply(200, expected); + + const resp = await client.v2.reserved_ips.byReserved_ip("192.0.2.1").actions.post({"type": "unassign"}); + expect(resp).toEqual(typeExpected); + }); + + + +}); \ No newline at end of file diff --git a/tests/mocked/sizes.test.js b/tests/mocked/sizes.test.js new file mode 100644 index 000000000..6f2e00d89 --- /dev/null +++ b/tests/mocked/sizes.test.js @@ -0,0 +1,98 @@ +/** + * Mock tests for the Sizes API + */ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Sizes API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should mock the sizes list operation", async () => { + const expected = { + sizes: [ + { + slug: "s-1vcpu-1gb", + memory: 1024, + vcpus: 1, + disk: 25, + transfer: 1, + price_monthly: 5, + price_hourly: 0.00743999984115362, + regions: [ + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + ], + available: true, + description: "Basic", + }, + ], + links: { + pages: { + last: "https://api.digitalocean.com/v2/sizes?page=64&per_page=1", + next: "https://api.digitalocean.com/v2/sizes?page=2&per_page=1", + }, + }, + meta: { total: 64 }, + }; + const typeExpected = { + sizes: [ + { + slug: "s-1vcpu-1gb", + memory: 1024, + vcpus: 1, + disk: 25, + transfer: 1, + priceMonthly: 5, + priceHourly: 0.00743999984115362, + regions: [ + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + ], + available: true, + description: "Basic", + }, + ], + links: { + pages: { + additionalData: { + last: "https://api.digitalocean.com/v2/sizes?page=64&per_page=1", + next: "https://api.digitalocean.com/v2/sizes?page=2&per_page=1", + } + }, + }, + meta: { total: 64 }, + }; + nock(baseUrl).get("/v2/sizes").reply(200, expected); + const listResp = await client.v2.sizes.get(); + expect(listResp).toEqual(typeExpected); + }); +}); diff --git a/tests/mocked/sizes.test.ts b/tests/mocked/sizes.test.ts new file mode 100644 index 000000000..7b107c8e5 --- /dev/null +++ b/tests/mocked/sizes.test.ts @@ -0,0 +1,105 @@ +/** + * Mock tests for the Sizes API + */ + +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Sizes API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should mock the sizes list operation", async () => { + const expected = { + sizes: [ + { + slug: "s-1vcpu-1gb", + memory: 1024, + vcpus: 1, + disk: 25, + transfer: 1, + price_monthly: 5, + price_hourly: 0.00743999984115362, + regions: [ + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + ], + available: true, + description: "Basic", + }, + ], + links: { + pages: { + last: "https://api.digitalocean.com/v2/sizes?page=64&per_page=1", + next: "https://api.digitalocean.com/v2/sizes?page=2&per_page=1", + }, + }, + meta: { total: 64 }, + }; + + const typeExpected = { + sizes: [ + { + slug: "s-1vcpu-1gb", + memory: 1024, + vcpus: 1, + disk: 25, + transfer: 1, + priceMonthly: 5, + priceHourly: 0.00743999984115362, + regions: [ + "ams2", + "ams3", + "blr1", + "fra1", + "lon1", + "nyc1", + "nyc2", + "nyc3", + "sfo1", + "sfo2", + "sfo3", + "sgp1", + "tor1", + ], + available: true, + description: "Basic", + }, + ], + links: { + pages: { + additionalData : { + last: "https://api.digitalocean.com/v2/sizes?page=64&per_page=1", + next: "https://api.digitalocean.com/v2/sizes?page=2&per_page=1", + } + }, + }, + meta: { total: 64 }, + }; + + nock(baseUrl).get("/v2/sizes").reply(200, expected); + + const listResp = await client.v2.sizes.get(); + expect(listResp).toEqual(typeExpected); + }); +}); \ No newline at end of file diff --git a/tests/mocked/snapshots.test.js b/tests/mocked/snapshots.test.js new file mode 100644 index 000000000..0a151311d --- /dev/null +++ b/tests/mocked/snapshots.test.js @@ -0,0 +1,114 @@ +/** + * Mock tests for the Snapshots API + */ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Snapshots API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should mock the snapshots list operation", async () => { + const expected = { + snapshots: [ + { + id: "6372321", + name: "web-01-1595954862243", + created_at: "2020-07-28T16:47:44Z", + regions: ["nyc3", "sfo3"], + resource_id: "200776916", + resource_type: "droplet", + min_disk_size: 25, + size_gigabytes: 2.34, + tags: ["web", "env:prod"], + }, + { + id: "fbe805e8-866b-11e6-96bf-000f53315a41", + name: "pvc-01-1595954862243", + created_at: "2019-09-28T23:14:30Z", + regions: ["nyc1"], + resource_id: "89bcc42f-85cf-11e6-a004-000f53315871", + resource_type: "volume", + min_disk_size: 2, + size_gigabytes: 0.1008, + tags: ["k8s"], + }, + ], + links: {}, + meta: { total: 2 }, + }; + const typeExpected = { + snapshots: [ + { + id: "6372321", + name: "web-01-1595954862243", + createdAt: new Date("2020-07-28T16:47:44Z"), + regions: ["nyc3", "sfo3"], + resourceId: "200776916", + resourceType: "droplet", + minDiskSize: 25, + sizeGigabytes: 2.34, + tags: ["web", "env:prod"], + }, + { + id: "fbe805e8-866b-11e6-96bf-000f53315a41", + name: "pvc-01-1595954862243", + createdAt: new Date("2019-09-28T23:14:30Z"), + regions: ["nyc1"], + resourceId: "89bcc42f-85cf-11e6-a004-000f53315871", + resourceType: "volume", + minDiskSize: 2, + sizeGigabytes: 0.1008, + tags: ["k8s"], + }, + ], + links: {}, + meta: { total: 2 }, + }; + nock(baseUrl).get("/v2/snapshots").reply(200, expected); + const listResp = await client.v2.snapshots.get(); + expect(listResp).toEqual(typeExpected); + }); + it("should retrieve an existing snapshot", async () => { + const expected = { + snapshot: { + id: "6372321", + name: "web-01-1595954862243", + created_at: "2020-07-28T16:47:44Z", + regions: ["nyc3", "sfo3"], + min_disk_size: 25, + size_gigabytes: 2.34, + resource_id: "200776916", + resource_type: "droplet", + tags: ["web", "env:prod"], + }, + }; + const typeExpected = { + snapshot: { + id: "6372321", + name: "web-01-1595954862243", + createdAt: new Date("2020-07-28T16:47:44Z"), + regions: ["nyc3", "sfo3"], + minDiskSize: 25, + sizeGigabytes: 2.34, + resourceId: "200776916", + resourceType: "droplet", + tags: ["web", "env:prod"], + }, + }; + nock(baseUrl).get("/v2/snapshots/6372321").reply(200, expected); + const getResp = await client.v2.snapshots.bySnapshot_id("6372321").get(); + expect(getResp).toEqual(typeExpected); + }); + it("should delete a snapshot", async () => { + nock(baseUrl).delete("/v2/snapshots/6372321").reply(204); + const delResp = await client.v2.snapshots.bySnapshot_id("6372321").delete(); + expect(delResp).toBeUndefined(); + }); +}); diff --git a/tests/mocked/snapshots.test.ts b/tests/mocked/snapshots.test.ts new file mode 100644 index 000000000..5f4c15b10 --- /dev/null +++ b/tests/mocked/snapshots.test.ts @@ -0,0 +1,127 @@ +/** + * Mock tests for the Snapshots API + */ + +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Snapshots API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should mock the snapshots list operation", async () => { + const expected = { + snapshots: [ + { + id: "6372321", + name: "web-01-1595954862243", + created_at: "2020-07-28T16:47:44Z", + regions: ["nyc3", "sfo3"], + resource_id: "200776916", + resource_type: "droplet", + min_disk_size: 25, + size_gigabytes: 2.34, + tags: ["web", "env:prod"], + }, + { + id: "fbe805e8-866b-11e6-96bf-000f53315a41", + name: "pvc-01-1595954862243", + created_at: "2019-09-28T23:14:30Z", + regions: ["nyc1"], + resource_id: "89bcc42f-85cf-11e6-a004-000f53315871", + resource_type: "volume", + min_disk_size: 2, + size_gigabytes: 0.1008, + tags: ["k8s"], + }, + ], + links: {}, + meta: { total: 2 }, + }; + + const typeExpected = { + snapshots: [ + { + id: "6372321", + name: "web-01-1595954862243", + createdAt: new Date("2020-07-28T16:47:44Z"), + regions: ["nyc3", "sfo3"], + resourceId: "200776916", + resourceType: "droplet", + minDiskSize: 25, + sizeGigabytes: 2.34, + tags: ["web", "env:prod"], + }, + { + id: "fbe805e8-866b-11e6-96bf-000f53315a41", + name: "pvc-01-1595954862243", + createdAt: new Date("2019-09-28T23:14:30Z"), + regions: ["nyc1"], + resourceId: "89bcc42f-85cf-11e6-a004-000f53315871", + resourceType: "volume", + minDiskSize: 2, + sizeGigabytes: 0.1008, + tags: ["k8s"], + }, + ], + links: {}, + meta: { total: 2 }, + }; + + nock(baseUrl).get("/v2/snapshots").reply(200, expected); + + const listResp = await client.v2.snapshots.get(); + expect(listResp).toEqual(typeExpected); + }); + + it("should retrieve an existing snapshot", async () => { + const expected = { + snapshot: { + id: "6372321", + name: "web-01-1595954862243", + created_at: "2020-07-28T16:47:44Z", + regions: ["nyc3", "sfo3"], + min_disk_size: 25, + size_gigabytes: 2.34, + resource_id: "200776916", + resource_type: "droplet", + tags: ["web", "env:prod"], + }, + }; + + const typeExpected = { + snapshot: { + id: "6372321", + name: "web-01-1595954862243", + createdAt: new Date("2020-07-28T16:47:44Z"), + regions: ["nyc3", "sfo3"], + minDiskSize: 25, + sizeGigabytes: 2.34, + resourceId: "200776916", + resourceType: "droplet", + tags: ["web", "env:prod"], + }, + }; + + nock(baseUrl).get("/v2/snapshots/6372321").reply(200, expected); + + const getResp = await client.v2.snapshots.bySnapshot_id("6372321").get(); + expect(getResp).toEqual(typeExpected); + }); + + it("should delete a snapshot", async () => { + nock(baseUrl).delete("/v2/snapshots/6372321").reply(204); + + const delResp = await client.v2.snapshots.bySnapshot_id("6372321").delete(); + expect(delResp).toBeUndefined(); + }); +}); \ No newline at end of file diff --git a/tests/mocked/sshKeys.test.js b/tests/mocked/sshKeys.test.js new file mode 100644 index 000000000..042474186 --- /dev/null +++ b/tests/mocked/sshKeys.test.js @@ -0,0 +1,141 @@ +/** + * Mock tests for the SSH Keys API resource + */ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("SSH Keys API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should mock the SSH keys list operation", async () => { + const expected = { + ssh_keys: [ + { + id: 1234, + public_key: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + { + id: 5678, + public_key: "ssh-rsa longKeyString test", + name: "test", + fingerprint: "0a:56:d2:46:64:64:12:95:34:ce:e7:vf:0f:c8:5a:d3", + }, + ], + links: { pages: {} }, + meta: { total: 2 }, + }; + const typeExpected = { + sshKeys: [ + { + id: 1234, + publicKey: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + { + id: 5678, + publicKey: "ssh-rsa longKeyString test", + name: "test", + fingerprint: "0a:56:d2:46:64:64:12:95:34:ce:e7:vf:0f:c8:5a:d3", + }, + ], + links: { pages: {} }, + meta: { total: 2 }, + }; + nock(baseUrl).get("/v2/account/keys").reply(200, expected); + const keys = await client.v2.account.keys.get(); + expect(keys).toEqual(typeExpected); + }); + it("should retrieve an SSH key by ID", async () => { + const expected = { + ssh_key: { + id: 1234, + public_key: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + const typeExpected = { + sshKey: { + id: 1234, + publicKey: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + nock(baseUrl).get("/v2/account/keys/1234").reply(200, expected); + const key = await client.v2.account.keys.bySsh_key_identifier("1234").get(); + expect(key).toEqual(typeExpected); + }); + it("should create an SSH key", async () => { + const expected = { + ssh_key: { + id: 1234, + public_key: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + const typeExpected = { + sshKey: { + id: 1234, + publicKey: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + nock(baseUrl).post("/v2/account/keys", { name: "key", public_key: "ssh-rsa aaaBBBccc123 key" }).reply(201, expected); + const key = await client.v2.account.keys.post({ name: "key", publicKey: "ssh-rsa aaaBBBccc123 key" }); + expect(key).toEqual(typeExpected); + }); + it("should update an SSH key", async () => { + const expected = { + ssh_key: { + id: 1234, + public_key: "ssh-rsa aaaBBBccc123 key", + name: "new-name", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + const typeExpected = { + sshKey: { + id: 1234, + publicKey: "ssh-rsa aaaBBBccc123 key", + name: "new-name", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + nock(baseUrl).put("/v2/account/keys/1234", { name: "new-name" }).reply(200, expected); + const key = await client.v2.account.keys.bySsh_key_identifier("1234").put({ name: "new-name" }); + expect(key).toEqual(typeExpected); + }); + it("should delete an SSH key", async () => { + nock(baseUrl).delete("/v2/account/keys/1234").reply(204); + const delResp = await client.v2.account.keys.bySsh_key_identifier("1234").delete(); + expect(delResp).toBeUndefined(); + }); + it("should handle error response for SSH key deletion", async () => { + const expected = { + id: "not_found", + message: "The resource you requested could not be found.", + }; + nock(baseUrl).delete("/v2/account/keys/1234").reply(404, expected); + try { + await client.v2.account.keys.bySsh_key_identifier("1234").delete(); + } + catch (error) { + if (typeof error === "object" && error !== null && "messageEscaped" in error) { + expect(error.messageEscaped).toEqual(expected.message); + } + } + }); +}); diff --git a/tests/mocked/sshKeys.test.ts b/tests/mocked/sshKeys.test.ts new file mode 100644 index 000000000..53f76715b --- /dev/null +++ b/tests/mocked/sshKeys.test.ts @@ -0,0 +1,164 @@ +/** + * Mock tests for the SSH Keys API resource + */ + +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("SSH Keys API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should mock the SSH keys list operation", async () => { + const expected = { + ssh_keys: [ + { + id: 1234, + public_key: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + { + id: 5678, + public_key: "ssh-rsa longKeyString test", + name: "test", + fingerprint: "0a:56:d2:46:64:64:12:95:34:ce:e7:vf:0f:c8:5a:d3", + }, + ], + links: { pages: {} }, + meta: { total: 2 }, + }; + + const typeExpected = { + sshKeys: [ + { + id: 1234, + publicKey: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + { + id: 5678, + publicKey: "ssh-rsa longKeyString test", + name: "test", + fingerprint: "0a:56:d2:46:64:64:12:95:34:ce:e7:vf:0f:c8:5a:d3", + }, + ], + links: { pages: {} }, + meta: { total: 2 }, + }; + + nock(baseUrl).get("/v2/account/keys").reply(200, expected); + + const keys = await client.v2.account.keys.get(); + expect(keys).toEqual(typeExpected); + }); + + it("should retrieve an SSH key by ID", async () => { + const expected = { + ssh_key: { + id: 1234, + public_key: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + + const typeExpected = { + sshKey: { + id: 1234, + publicKey: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + + nock(baseUrl).get("/v2/account/keys/1234").reply(200, expected); + + const key = await client.v2.account.keys.bySsh_key_identifier("1234").get(); + expect(key).toEqual(typeExpected); + }); + + it("should create an SSH key", async () => { + const expected = { + ssh_key: { + id: 1234, + public_key: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + + const typeExpected = { + sshKey: { + id: 1234, + publicKey: "ssh-rsa aaaBBBccc123 key", + name: "key", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + + nock(baseUrl).post("/v2/account/keys", { name: "key", public_key: "ssh-rsa aaaBBBccc123 key" }).reply(201, expected); + + const key = await client.v2.account.keys.post({ name: "key", publicKey: "ssh-rsa aaaBBBccc123 key" }); + expect(key).toEqual(typeExpected); + }); + + it("should update an SSH key", async () => { + const expected = { + ssh_key: { + id: 1234, + public_key: "ssh-rsa aaaBBBccc123 key", + name: "new-name", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + + const typeExpected = { + sshKey: { + id: 1234, + publicKey: "ssh-rsa aaaBBBccc123 key", + name: "new-name", + fingerprint: "17:23:a1:4f:55:4b:59:c6:ad:f7:69:dc:4e:85:e4:8a", + }, + }; + + nock(baseUrl).put("/v2/account/keys/1234", { name: "new-name" }).reply(200, expected); + + const key = await client.v2.account.keys.bySsh_key_identifier("1234").put({ name: "new-name" }); + expect(key).toEqual(typeExpected); + }); + + it("should delete an SSH key", async () => { + nock(baseUrl).delete("/v2/account/keys/1234").reply(204); + + const delResp = await client.v2.account.keys.bySsh_key_identifier("1234").delete(); + expect(delResp).toBeUndefined(); + }); + + it("should handle error response for SSH key deletion", async () => { + const expected = { + id: "not_found", + message: "The resource you requested could not be found.", + }; + + nock(baseUrl).delete("/v2/account/keys/1234").reply(404, expected); + + try { + await client.v2.account.keys.bySsh_key_identifier("1234").delete(); + } catch (error) { + if (typeof error === "object" && error !== null && "messageEscaped" in error) { + expect(error.messageEscaped).toEqual(expected.message); + } + } + }); +}); \ No newline at end of file diff --git a/tests/mocked/tags.test.js b/tests/mocked/tags.test.js new file mode 100644 index 000000000..c923d9897 --- /dev/null +++ b/tests/mocked/tags.test.js @@ -0,0 +1,187 @@ +/** + * Mock tests for the Tags API + */ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("Tags API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should mock the tags list operation", async () => { + const expected = { + tags: [ + { + name: "tag-with-resources", + resources: { + count: 3, + last_tagged_uri: "https://api.digitalocean.com/v2/droplets/123", + droplets: { + count: 2, + last_tagged_uri: "https://api.digitalocean.com/v2/droplets/123", + }, + images: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + }, + volumes: { count: 0 }, + volume_snapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + { + name: "tag-with-no-resources", + resources: { + count: 0, + droplets: { count: 0 }, + images: { count: 0 }, + volumes: { count: 0 }, + volume_snapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + ], + links: {}, + meta: { total: 2 }, + }; + const typeExpected = { + tags: [ + { + name: "tag-with-resources", + resources: { + count: 3, + lastTaggedUri: "https://api.digitalocean.com/v2/droplets/123", + droplets: { + count: 2, + lastTaggedUri: "https://api.digitalocean.com/v2/droplets/123", + }, + additionalData: { + images: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + }, + }, + volumes: { count: 0 }, + volumeSnapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + { + name: "tag-with-no-resources", + resources: { + count: 0, + droplets: { count: 0 }, + additionalData: { + images: { count: 0 }, + }, + volumes: { count: 0 }, + volumeSnapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + ], + links: {}, + meta: { total: 2 }, + }; + nock(baseUrl).get("/v2/tags").reply(200, expected); + const tags = await client.v2.tags.get(); + expect(tags).toEqual(typeExpected); + }); + it("should retrieve a tag by name", async () => { + const expected = { + tag: { + name: "example-tag", + resources: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + droplets: { count: 0 }, + images: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + }, + volumes: { count: 0 }, + volume_snapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + }; + const typeExpected = { + tag: { + name: "example-tag", + resources: { + count: 1, + lastTaggedUri: "https://api.digitalocean.com/v2/images/1234", + droplets: { count: 0 }, + additionalData: { + images: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + }, + }, + volumes: { count: 0 }, + volumeSnapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + }; + nock(baseUrl).get("/v2/tags/example-tag").reply(200, expected); + const tag = await client.v2.tags.byTag_id("example-tag").get(); + expect(tag).toEqual(typeExpected); + }); + it("should create a tag", async () => { + const expected = { + tag: { + name: "example-tag", + resources: { + count: 0, + droplets: { count: 0 }, + images: { count: 0 }, + volumes: { count: 0 }, + volume_snapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + }; + const typeExpected = { + tag: { + name: "example-tag", + resources: { + count: 0, + droplets: { count: 0 }, + additionalData: { + images: { count: 0 }, + }, + volumes: { count: 0 }, + volumeSnapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + }; + nock(baseUrl).post("/v2/tags", { name: "example-tag" }).reply(201, expected); + const tag = await client.v2.tags.post({ name: "example-tag" }); + expect(tag).toEqual(typeExpected); + }); + it("should delete a tag", async () => { + nock(baseUrl).delete("/v2/tags/example-tag").reply(204); + const delResp = await client.v2.tags.byTag_id("example-tag").delete(); + expect(delResp).toBeUndefined(); + }); + it("should unassign resources from a tag", async () => { + nock(baseUrl).delete("/v2/tags/example-tag/resources").reply(204); + const req = { + resources: [ + { resourceId: "1234", resourceType: "droplet" }, + { resourceId: "5678", resourceType: "image" }, + { resourceId: "aaa-bbb-ccc-111", resourceType: "volume" }, + ], + }; + const unassignResp = await client.v2.tags.byTag_id("example-tag").resources.delete(req); + expect(unassignResp).toBeUndefined(); + }); +}); diff --git a/tests/mocked/tags.test.ts b/tests/mocked/tags.test.ts new file mode 100644 index 000000000..6990ac481 --- /dev/null +++ b/tests/mocked/tags.test.ts @@ -0,0 +1,209 @@ +/** + * Mock tests for the Tags API + */ + +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +import { Tags_resource } from "../../src/dots/models/index.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("Tags API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should mock the tags list operation", async () => { + const expected = { + tags: [ + { + name: "tag-with-resources", + resources: { + count: 3, + last_tagged_uri: "https://api.digitalocean.com/v2/droplets/123", + droplets: { + count: 2, + last_tagged_uri: "https://api.digitalocean.com/v2/droplets/123", + }, + images: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + }, + volumes: { count: 0 }, + volume_snapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + { + name: "tag-with-no-resources", + resources: { + count: 0, + droplets: { count: 0 }, + images: { count: 0 }, + volumes: { count: 0 }, + volume_snapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + ], + links: {}, + meta: { total: 2 }, + }; + + const typeExpected = { + tags: [ + { + name: "tag-with-resources", + resources: { + + count: 3, + lastTaggedUri: "https://api.digitalocean.com/v2/droplets/123", + droplets: { + count: 2, + lastTaggedUri: "https://api.digitalocean.com/v2/droplets/123", + }, + additionalData:{ + images: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + }, + }, + volumes: { count: 0 }, + volumeSnapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + { + name: "tag-with-no-resources", + resources: { + count: 0, + droplets: { count: 0 }, + additionalData:{ + images: { count: 0 }, + }, + volumes: { count: 0 }, + volumeSnapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + ], + links: {}, + meta: { total: 2 }, + }; + + nock(baseUrl).get("/v2/tags").reply(200, expected); + + const tags = await client.v2.tags.get(); + expect(tags).toEqual(typeExpected); + }); + + it("should retrieve a tag by name", async () => { + const expected = { + tag: { + name: "example-tag", + resources: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + droplets: { count: 0 }, + images: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + }, + volumes: { count: 0 }, + volume_snapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + }; + + const typeExpected = { + tag: { + name: "example-tag", + resources: { + count: 1, + lastTaggedUri: "https://api.digitalocean.com/v2/images/1234", + droplets: { count: 0 }, + additionalData: { + images: { + count: 1, + last_tagged_uri: "https://api.digitalocean.com/v2/images/1234", + }, + }, + volumes: { count: 0 }, + volumeSnapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + }; + + nock(baseUrl).get("/v2/tags/example-tag").reply(200, expected); + + const tag = await client.v2.tags.byTag_id("example-tag").get(); + expect(tag).toEqual(typeExpected); + }); + + it("should create a tag", async () => { + const expected = { + tag: { + name: "example-tag", + resources: { + count: 0, + droplets: { count: 0 }, + images: { count: 0 }, + volumes: { count: 0 }, + volume_snapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + }; + + const typeExpected = { + tag: { + name: "example-tag", + resources: { + count: 0, + droplets: { count: 0 }, + additionalData : { + images: { count: 0 }, + }, + volumes: { count: 0 }, + volumeSnapshots: { count: 0 }, + databases: { count: 0 }, + }, + }, + }; + + nock(baseUrl).post("/v2/tags", { name: "example-tag" }).reply(201, expected); + + const tag = await client.v2.tags.post({ name: "example-tag" }); + expect(tag).toEqual(typeExpected); + }); + + it("should delete a tag", async () => { + nock(baseUrl).delete("/v2/tags/example-tag").reply(204); + + const delResp = await client.v2.tags.byTag_id("example-tag").delete(); + expect(delResp).toBeUndefined(); + }); + + + it("should unassign resources from a tag", async () => { + nock(baseUrl).delete("/v2/tags/example-tag/resources").reply(204); + + const req :Tags_resource= { + resources: [ + { resourceId: "1234", resourceType: "droplet" }, + { resourceId: "5678", resourceType: "image" }, + { resourceId: "aaa-bbb-ccc-111", resourceType: "volume" }, + ], + }; + + const unassignResp = await client.v2.tags.byTag_id("example-tag").resources.delete(req); + expect(unassignResp).toBeUndefined(); + }); +}); \ No newline at end of file diff --git a/tests/mocked/vpc.test.js b/tests/mocked/vpc.test.js new file mode 100644 index 000000000..18b3bf0d7 --- /dev/null +++ b/tests/mocked/vpc.test.js @@ -0,0 +1,258 @@ +/** + * Mock tests for the VPCs API + */ +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); +describe("VPCs API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + it("should mock the VPC creation operation", async () => { + const expected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + default: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + created_at: "2020-03-13T19:20:47.442049222Z", + }, + }; + const typeExpected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + defaultEscaped: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + }; + nock(baseUrl).post("/v2/vpcs", { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + }).reply(201, expected); + const createResp = await client.v2.vpcs.post({ + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + }); + expect(createResp).toEqual(typeExpected); + }); + it("should mock the VPC list operation", async () => { + const expected = { + vpcs: [ + { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:vpc:5a4981aa-9653-4bd1-bef5-d6bff52042e4", + default: true, + created_at: "2020-03-13T19:20:47.442049222Z", + }, + ], + links: {}, + meta: { total: 1 }, + }; + const typeExpected = { + vpcs: [ + { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:vpc:5a4981aa-9653-4bd1-bef5-d6bff52042e4", + defaultEscaped: true, + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + ], + links: {}, + meta: { total: 1 }, + }; + nock(baseUrl).get("/v2/vpcs").reply(200, expected); + const listResp = await client.v2.vpcs.get(); + expect(listResp).toEqual(typeExpected); + }); + it("should mock the VPC retrieval operation", async () => { + const expected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + default: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + created_at: "2020-03-13T19:20:47.442049222Z", + }, + }; + const typeExpected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + defaultEscaped: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + }; + nock(baseUrl).get("/v2/vpcs/5a4981aa-9653-4bd1-bef5-d6bff52042e4").reply(200, expected); + const getResp = await client.v2.vpcs.byVpc_id("5a4981aa-9653-4bd1-bef5-d6bff52042e4").get(); + expect(getResp).toEqual(typeExpected); + }); + it("should mock the VPC update operation", async () => { + const expected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + default: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + created_at: "2020-03-13T19:20:47.442049222Z", + }, + }; + const typeExpected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + defaultEscaped: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + }; + nock(baseUrl).put("/v2/vpcs/5a4981aa-9653-4bd1-bef5-d6bff52042e4", { + name: "env.prod-vpc", + description: "VPC for production environment", + default: true, + }).reply(200, expected); + const updateResp = await client.v2.vpcs.byVpc_id("5a4981aa-9653-4bd1-bef5-d6bff52042e4").put({ + name: "env.prod-vpc", + description: "VPC for production environment", + defaultEscaped: true, + }); + expect(updateResp).toEqual(typeExpected); + }); + it("should mock the VPC patch operation", async () => { + const expected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + default: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + created_at: "2020-03-13T19:20:47.442049222Z", + }, + }; + const typeExpected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + defaultEscaped: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + }; + nock(baseUrl).patch("/v2/vpcs/5a4981aa-9653-4bd1-bef5-d6bff52042e4", { + name: "env.prod-vpc", + description: "VPC for production environment", + default: true, + }).reply(200, expected); + const patchResp = await client.v2.vpcs.byVpc_id("5a4981aa-9653-4bd1-bef5-d6bff52042e4").patch({ + name: "env.prod-vpc", + description: "VPC for production environment", + defaultEscaped: true, + }); + expect(patchResp).toEqual(typeExpected); + }); + it("should mock the VPC deletion operation", async () => { + nock(baseUrl).delete("/v2/vpcs/5a4981aa-9653-4bd1-bef5-d6bff52042e4").reply(204); + const delResp = await client.v2.vpcs.byVpc_id("5a4981aa-9653-4bd1-bef5-d6bff52042e4").delete(); + expect(delResp).toBeUndefined(); + }); + it("should mock the VPC list members operation", async () => { + const expected = { + members: [ + { + urn: "do:loadbalancer:fb294d78-d193-4cb2-8737-ea620993591b", + name: "nyc1-load-balancer-01", + created_at: "2020-03-13T19:30:48Z", + }, + { + urn: "do:dbaas:13f7a2f6-43df-4c4a-8129-8733267ddeea", + name: "db-postgresql-nyc1-55986", + created_at: "2020-03-13T19:30:18Z", + }, + { + urn: "do:kubernetes:da39d893-96e1-4e4d-971d-1fdda33a46b1", + name: "k8s-nyc1-1584127772221", + created_at: "2020-03-13T19:30:16Z", + }, + { + urn: "do:droplet:86e29982-03a7-4946-8a07-a0114dff8754", + name: "ubuntu-s-1vcpu-1gb-nyc1-01", + created_at: "2020-03-13T19:29:20Z", + }, + ], + links: {}, + meta: { total: 4 }, + }; + const typeExpected = { + members: [ + { + urn: "do:loadbalancer:fb294d78-d193-4cb2-8737-ea620993591b", + name: "nyc1-load-balancer-01", + createdAt: "2020-03-13T19:30:48Z", + }, + { + urn: "do:dbaas:13f7a2f6-43df-4c4a-8129-8733267ddeea", + name: "db-postgresql-nyc1-55986", + createdAt: "2020-03-13T19:30:18Z", + }, + { + urn: "do:kubernetes:da39d893-96e1-4e4d-971d-1fdda33a46b1", + name: "k8s-nyc1-1584127772221", + createdAt: "2020-03-13T19:30:16Z", + }, + { + urn: "do:droplet:86e29982-03a7-4946-8a07-a0114dff8754", + name: "ubuntu-s-1vcpu-1gb-nyc1-01", + createdAt: "2020-03-13T19:29:20Z", + }, + ], + links: {}, + meta: { total: 4 }, + }; + nock(baseUrl).get("/v2/vpcs/1/members").reply(200, expected); + const listResp = await client.v2.vpcs.byVpc_id("1").members.get(); + expect(listResp).toEqual(typeExpected); + }); +}); diff --git a/tests/mocked/vpc.test.ts b/tests/mocked/vpc.test.ts new file mode 100644 index 000000000..d31a5e665 --- /dev/null +++ b/tests/mocked/vpc.test.ts @@ -0,0 +1,289 @@ +/** + * Mock tests for the VPCs API + */ + +import nock from "nock"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import { createDigitalOceanClient } from "../../src/dots/digitalOceanClient.js"; +import { DigitalOceanApiKeyAuthenticationProvider } from "../../src/dots/DigitalOceanApiKeyAuthenticationProvider.js"; + +const baseUrl = "https://api.digitalocean.com"; +const token = "test-token"; +const authProvider = new DigitalOceanApiKeyAuthenticationProvider(token); +const adapter = new FetchRequestAdapter(authProvider); +const client = createDigitalOceanClient(adapter); + +describe("VPCs API Mock Tests", () => { + afterEach(() => { + nock.cleanAll(); + }); + + it("should mock the VPC creation operation", async () => { + const expected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + default: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + created_at: "2020-03-13T19:20:47.442049222Z", + }, + }; + + const typeExpected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + defaultEscaped: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + }; + + nock(baseUrl).post("/v2/vpcs", { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + }).reply(201, expected); + + const createResp = await client.v2.vpcs.post({ + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + }); + + expect(createResp).toEqual(typeExpected); + }); + + it("should mock the VPC list operation", async () => { + const expected = { + vpcs: [ + { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:vpc:5a4981aa-9653-4bd1-bef5-d6bff52042e4", + default: true, + created_at: "2020-03-13T19:20:47.442049222Z", + }, + ], + links: {}, + meta: { total: 1 }, + }; + + const typeExpected = { + vpcs: [ + { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:vpc:5a4981aa-9653-4bd1-bef5-d6bff52042e4", + defaultEscaped: true, + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + ], + links: {}, + meta: { total: 1 }, + }; + + nock(baseUrl).get("/v2/vpcs").reply(200, expected); + + const listResp = await client.v2.vpcs.get(); + expect(listResp).toEqual(typeExpected); + }); + + it("should mock the VPC retrieval operation", async () => { + const expected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + default: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + created_at: "2020-03-13T19:20:47.442049222Z", + }, + }; + + const typeExpected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + defaultEscaped: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + }; + + nock(baseUrl).get("/v2/vpcs/5a4981aa-9653-4bd1-bef5-d6bff52042e4").reply(200, expected); + + const getResp = await client.v2.vpcs.byVpc_id("5a4981aa-9653-4bd1-bef5-d6bff52042e4").get(); + expect(getResp).toEqual(typeExpected); + }); + + it("should mock the VPC update operation", async () => { + const expected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + default: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + created_at: "2020-03-13T19:20:47.442049222Z", + }, + }; + + const typeExpected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + defaultEscaped: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + }; + + nock(baseUrl).put("/v2/vpcs/5a4981aa-9653-4bd1-bef5-d6bff52042e4", { + name: "env.prod-vpc", + description: "VPC for production environment", + default: true, + }).reply(200, expected); + + const updateResp = await client.v2.vpcs.byVpc_id("5a4981aa-9653-4bd1-bef5-d6bff52042e4").put({ + name: "env.prod-vpc", + description: "VPC for production environment", + defaultEscaped: true, + }); + + expect(updateResp).toEqual(typeExpected); + }); + + it("should mock the VPC patch operation", async () => { + const expected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ip_range: "10.10.10.0/24", + default: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + created_at: "2020-03-13T19:20:47.442049222Z", + }, + }; + + const typeExpected = { + vpc: { + name: "env.prod-vpc", + description: "VPC for production environment", + region: "nyc1", + ipRange: "10.10.10.0/24", + defaultEscaped: true, + id: "5a4981aa-9653-4bd1-bef5-d6bff52042e4", + urn: "do:droplet:13457723", + createdAt: new Date("2020-03-13T19:20:47.442049222Z"), + }, + }; + + nock(baseUrl).patch("/v2/vpcs/5a4981aa-9653-4bd1-bef5-d6bff52042e4", { + name: "env.prod-vpc", + description: "VPC for production environment", + default: true, + }).reply(200, expected); + + const patchResp = await client.v2.vpcs.byVpc_id("5a4981aa-9653-4bd1-bef5-d6bff52042e4").patch({ + name: "env.prod-vpc", + description: "VPC for production environment", + defaultEscaped: true, + }); + + expect(patchResp).toEqual(typeExpected); + }); + + it("should mock the VPC deletion operation", async () => { + nock(baseUrl).delete("/v2/vpcs/5a4981aa-9653-4bd1-bef5-d6bff52042e4").reply(204); + + const delResp = await client.v2.vpcs.byVpc_id("5a4981aa-9653-4bd1-bef5-d6bff52042e4").delete(); + expect(delResp).toBeUndefined(); + }); + + it("should mock the VPC list members operation", async () => { + const expected = { + members: [ + { + urn: "do:loadbalancer:fb294d78-d193-4cb2-8737-ea620993591b", + name: "nyc1-load-balancer-01", + created_at: "2020-03-13T19:30:48Z", + }, + { + urn: "do:dbaas:13f7a2f6-43df-4c4a-8129-8733267ddeea", + name: "db-postgresql-nyc1-55986", + created_at: "2020-03-13T19:30:18Z", + }, + { + urn: "do:kubernetes:da39d893-96e1-4e4d-971d-1fdda33a46b1", + name: "k8s-nyc1-1584127772221", + created_at: "2020-03-13T19:30:16Z", + }, + { + urn: "do:droplet:86e29982-03a7-4946-8a07-a0114dff8754", + name: "ubuntu-s-1vcpu-1gb-nyc1-01", + created_at: "2020-03-13T19:29:20Z", + }, + ], + links: {}, + meta: { total: 4 }, + }; + const typeExpected = { + members: [ + { + urn: "do:loadbalancer:fb294d78-d193-4cb2-8737-ea620993591b", + name: "nyc1-load-balancer-01", + createdAt: "2020-03-13T19:30:48Z", + }, + { + urn: "do:dbaas:13f7a2f6-43df-4c4a-8129-8733267ddeea", + name: "db-postgresql-nyc1-55986", + createdAt: "2020-03-13T19:30:18Z", + }, + { + urn: "do:kubernetes:da39d893-96e1-4e4d-971d-1fdda33a46b1", + name: "k8s-nyc1-1584127772221", + createdAt: "2020-03-13T19:30:16Z", + }, + { + urn: "do:droplet:86e29982-03a7-4946-8a07-a0114dff8754", + name: "ubuntu-s-1vcpu-1gb-nyc1-01", + createdAt: "2020-03-13T19:29:20Z", + }, + ], + links: {}, + meta: { total: 4 }, + }; + + nock(baseUrl).get("/v2/vpcs/1/members").reply(200, expected); + + const listResp = await client.v2.vpcs.byVpc_id("1").members.get(); + expect(listResp).toEqual(typeExpected); + }); +}); \ No newline at end of file