Skip to content

Commit

Permalink
install and test poetry (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
mythmon authored Sep 13, 2024
1 parent 695a8a1 commit a977bbb
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 26 deletions.
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ FROM buildpack-deps:bookworm AS base
ENV CACHEBUST=2024-09-06
RUN apt update

# Rust envvars
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
RUST_VERSION=1.81.0 \
VIRTUAL_ENV=/var/local/python-venv
ENV PATH=/usr/local/cargo/bin:$VIRTUAL_ENV/bin:$PATH
ENV PATH=/usr/local/cargo/bin:$VIRTUAL_ENV/bin:/root/.local/bin:$PATH

# == node ======================
FROM base AS node
Expand All @@ -33,6 +32,8 @@ RUN --mount=type=cache,target=/var/cache/apt,id=framework-runtime-python \
python3-wheel \
python3-dev \
python3-venv \
pipx \
&& pipx install poetry \
&& python3 -m venv $VIRTUAL_ENV

# == R ===========================
Expand Down
9 changes: 8 additions & 1 deletion bin/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { dirname } from "node:path";
import { run as runTests } from "node:test";
import { spec } from "node:test/reporters";

import { parseArgs } from "node:util";
const { values: { "only": argOnly } } = parseArgs({
options: {
"only": { type: "boolean" },
}
});

export async function buildTestImage() {
console.log("building image...");
let stdio = new StringStream();
Expand Down Expand Up @@ -34,7 +41,7 @@ const files = await glob(["tests/**/*.test.ts"], {
});

await buildTestImage();
runTests({ files, concurrency: true })
runTests({ files, concurrency: true, only: argOnly })
.on("test:fail", () => {
process.exitCode = 1;
})
Expand Down
75 changes: 60 additions & 15 deletions tests/dataloader-languages.test.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
import { test, describe } from "node:test";
import os from "node:os";
import assert from "node:assert/strict";
import {
assertSemver,
binaryOnPathTest,
binaryVersionTest,
runCommandInContainer,
} from "./index.ts";
import { cp, mkdtemp, rm } from "node:fs/promises";
import { join } from "node:path";

describe("Dataloader languages", () => {
describe("Python", () => {
binaryVersionTest({
binary: "python3",
semver: "^3.11",
prefix: "Python",
});

binaryOnPathTest({ binary: "pip" });

test(`A Python virtual environment is activated`, async () => {
// should not throw
await runCommandInContainer(["pip", "install", "requests"]);
});
});

describe("JavaScript", () => {
binaryVersionTest({ binary: "node", semver: "^20.17" });
binaryVersionTest({ binary: "npm", semver: "^10.5" });
Expand Down Expand Up @@ -57,6 +45,63 @@ describe("Dataloader languages", () => {
});
});

describe("Python", () => {
binaryVersionTest({
binary: "python3",
semver: "^3.11",
prefix: "Python",
});

binaryVersionTest({
binary: "pip",
semver: "^23.0.1",
extract: /^pip ([^ ]+) /,
});
binaryVersionTest({ binary: "pipx", semver: "^1.1.0" });
binaryVersionTest({
binary: "poetry",
semver: "^1.8.3",
prefix: "Poetry (version ",
suffix: ")",
});

test(`A Python virtual environment is activated`, async () => {
// should not throw
await runCommandInContainer(["pip", "install", "pip-install-test"]);
});

test(`Poetry can install dependencies in the virtualenv`, async () => {
let testDir = await mkdtemp(join(os.tmpdir(), "poetry-test-"));
try {
// This will install dependencies using Poetry, and then try to run `ls`
// in the installed dependency's package. If the package is not
// installed here, the `ls` command will exit non-zero and
// `runCommandInContainer` will throw.
await cp(
"./tests/fixtures/poetry-test/pyproject.toml",
`${testDir}/pyproject.toml`,
);
let res = await runCommandInContainer(
[
"sh",
"-c",
"poetry install; ls $(poetry env info --path)/lib/python3.11/site-packages/pip_install_test/__init__.py",
],
{
workingDir: "/poetry-test",
mounts: [{ host: testDir, container: "/poetry-test" }],
},
);
} finally {
try {
await rm(testDir, { recursive: true });
} catch {
/* ok */
}
}
});
});

binaryVersionTest({
binary: "Rscript",
semver: "^4.4.1",
Expand Down
6 changes: 6 additions & 0 deletions tests/fixtures/poetry-test/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[tool.poetry]
package-mode = false

[tool.poetry.dependencies]
python = "^3.11"
pip-install-test = "^0.5"
38 changes: 30 additions & 8 deletions tests/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { test, before } from "node:test";
import { resolve } from "node:path";
import assert from "node:assert";
import Dockerode from "dockerode";
import { Stream } from "node:stream";
Expand All @@ -13,6 +14,7 @@ export interface AssertBinaryVersionOptions {
semver: string;
extract?: RegExp;
prefix?: string;
suffix?: string;
expectStderr?: RegExp;
}

Expand All @@ -22,12 +24,16 @@ export async function binaryVersionTest({
semver,
extract,
prefix,
suffix,
expectStderr = /^$/,
}: AssertBinaryVersionOptions) {
await test(`${name} ${semver} is available`, async () => {
const res = await runCommandInContainer([binary, "--version"]);
assert.ok(res.stderr.match(expectStderr), `Expected stderr to match, got: ${res.stderr}`);
assertSemver(res.stdout, semver, { extract, prefix });
assert.ok(
res.stderr.match(expectStderr),
`Expected stderr to match, got: ${res.stderr}`,
);
assertSemver(res.stdout, semver, { extract, prefix, suffix });
});
}

Expand All @@ -54,7 +60,7 @@ export function assertSemver(
prefix,
suffix,
extract,
}: { prefix?: string; suffix?: string; extract?: RegExp } = {}
}: { prefix?: string; suffix?: string; extract?: RegExp } = {},
) {
actual = actual.trim();
if (prefix && actual.startsWith(prefix)) actual = actual.slice(prefix.length);
Expand All @@ -70,7 +76,7 @@ export function assertSemver(
actual = actual.trim();
assert.ok(
semverSatisfies(actual, expected),
`Expected semver match for ${expected}, got ${actual}`
`Expected semver match for ${expected}, got ${JSON.stringify(actual)}`,
);
}

Expand All @@ -82,12 +88,26 @@ function ensureDocker() {
before(ensureDocker);

export async function runCommandInContainer(
command: string[]
command: string[],
{
mounts = [],
workingDir = "/",
}: {
mounts?: { host: string; container: string}[];
workingDir?: string;
} = {},
): Promise<{ stdout: string; stderr: string }> {
const docker = ensureDocker();
const container = await docker.createContainer({
WorkingDir: workingDir,
Image: IMAGE_TAG,
Cmd: command,
HostConfig: {
Binds: mounts.map(
({ host, container }) =>
`${resolve(host)}:${container}`,
),
},
});
const stdout = new StringStream();
const stderr = new StringStream();
Expand All @@ -100,9 +120,11 @@ export async function runCommandInContainer(
await container.start();
const wait = (await container.wait()) as { StatusCode: number };
if (wait.StatusCode !== 0) {
throw new Error(`Command failed with status code ${wait.StatusCode}\n` +
`stdout:\n${stdout.string}\n\n` +
`stderr:\n${stderr.string}`);
throw new Error(
`Command failed with status code ${wait.StatusCode}\n` +
`stdout:\n${stdout.string}\n\n` +
`stderr:\n${stderr.string}`,
);
}
return { stdout: stdout.string, stderr: stderr.string };
}
Expand Down

0 comments on commit a977bbb

Please sign in to comment.