In dieser Übung werden wir einen Integration Test für unser Pulumi Programm aus Lab3 schreiben. Man nennt das auch "Deploy-Check-Destroy" Tests, weil wir mit Hilfe von Pulumi unsere Infrastruktur in ein vergängliches (ephemeral) Environment deployen, gegen dieses Environment unsere Tests ausführen und danach dieses Deployment wieder zerstören.
Bis vor Kurzem war es nur möglich, diese Art von Tests für Pulumi in Go mit dem dafür existierenden Integration-Testing Framework zu schreiben. Seit im April 2021 mit Pulumi 3.0 auch die Pulumi Automation API released wurde, können wir für die Integration Tests in unserer "gewohnten" Typescript Umgebung bleiben und Test-Frameworks und Tools aus diesem Ökosystem nutzen.
- Ein neues Pulumi Typescript Projekt mit Namen
lab4
erstellen. - Einen neuen Stack
dev
hinzufügen. - Dependencies hinzufügen:
npm i --save-dev chai cheerio mocha superagent ts-node
Als erstes implementieren wir unseren Test-Treiber. Die Pulumi Automation API erlaubt uns verinfacht gesagt, genau das in Programmcode zu machen, was die pulumi CLI macht, also up, destroy usw.
Wir erstellen eine neue Datei test-automation.ts in unserem Projektverzeichnis.
Im ersten Schritt erstellen wir LocalProgramArgs
, über diese können wir einen Stacknamen angeben und den Pfad zum Pulumi Programm, welches wir ausführen wollen. Der Stackname bekommt als Suffix einen zufällig generierten String.
import { LocalProgramArgs, LocalWorkspace, OutputMap } from "@pulumi/pulumi/automation";
import * as crypto from "crypto";
import * as upath from "upath";
const randomString = crypto.randomBytes(8).toString('hex');
const args: LocalProgramArgs = {
stackName: `integration-tests-${randomString}`,
workDir: upath.joinSafe(__dirname, "..", "Lab3-Snapshot"),
};
Als nächstes implementieren wir die Pendants zu pulumi up
und pulumi destroy
.
export async function deploy(): Promise<OutputMap> {
console.log(`deploying stack ${stack.name}`);
const stack = await LocalWorkspace.createOrSelectStack(args);
// Konfiguration für den Stack setzen
await stack.setConfig("azure-native:location", { value: "WestEurope" });
const up = await stack.up({ onOutput: console.log });
return up.outputs;
}
export async function destroy() {
console.log(`destroying stack ${stack.name}`);
const stack = await LocalWorkspace.createOrSelectStack(args);
await stack.destroy({ onOutput: console.log });
}
Zuletzt implementieren wir noch den ZUgriff auf die Outputs des Stacks.
export async function getOutputs(): Promise<OutputMap> {
const stack = await LocalWorkspace.createOrSelectStack(args);
var outputs = stack.outputs();
return outputs;
}
export default { deploy, getOutputs, destroy };
Wir implementieren einen before
und after
Hook, in dem wir unseren Test-Treiber aufrufen und dafür sorgen, dass das Deployment vor Ausführung der Tests durchgeführt und nach Abschluss der Tests wieder zerstört wird.
import { expect } from "chai";
import * as automation from "./test-automation";
import * as superagent from "superagent";
import * as cheerio from "cheerio";
before(async () => {
await automation.deploy();
});
after(async () => {
await automation.destroy();
});
Dann schreiben wir zwei einfache Tests. Einer soll überprüfen, dass ein Http Request mit der Url websiteUrl
, die in Lab3 als Output des Pulumi Programms exportiert wird, eine Http Response 200 OK liefert. Der andere prüft das HTML, welches unter websiteUrl
abgerufen wird.
describe("Deploying a static website", () => {
it("should return 200", async () => {
await automation
.getOutputs()
.then((result) => result.websiteUrl.value)
.then((url) => {
expect(url).to.be.a("string");
return superagent.get(url);
})
.then((response) => expect(response.statusCode).to.equal(200))
});
it("should return expected html", async () => {
await automation
.getOutputs()
.then((result) => result.websiteUrl.value)
.then((url) => {
expect(url).to.be.a("string");
return superagent.get(url);
})
.then((response) => response.text)
.then((html) => {
const dom = cheerio.load(html);
expect(dom("title").text()).to.equal("Pulumi Workshop Static Website");
});
});
});
Um unsere Tests ausführen zu können, müssen wir noch ein Test Script Command in der package.json
hinzufügen:
"scripts": {
"test": "./node_modules/mocha/bin/mocha --timeout=120000 -r ts-node/register ./test.ts"
}
npm run test