Skip to content

Commit 874977b

Browse files
Skip containerized tests in GHA on M-series mac
1 parent d7b42c9 commit 874977b

File tree

2 files changed

+184
-144
lines changed

2 files changed

+184
-144
lines changed

libs/checkpoint-mongodb/src/tests/migrations/1_object_metadata.test.ts

Lines changed: 151 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -18,159 +18,166 @@ import {
1818
uuid6,
1919
} from "@langchain/langgraph-checkpoint";
2020
import { Migration1ObjectMetadata } from "../../migrations/1_object_metadata.js";
21+
import { isSkippedCIEnvironment } from "../utils.js";
2122

2223
describe("1_object_metadata", () => {
23-
const dbName = "test_db";
24-
let container: StartedMongoDBContainer;
25-
let client: MongoClient;
26-
27-
beforeAll(async () => {
28-
container = await new MongoDBContainer("mongo:6.0.1").start();
29-
const connectionString = `mongodb://127.0.0.1:${container.getMappedPort(
30-
27017
31-
)}/${dbName}?directConnection=true`;
32-
client = new MongoClient(connectionString);
33-
});
34-
35-
afterAll(async () => {
36-
await client.close();
37-
await container.stop();
38-
});
39-
40-
describe("isApplicable", () => {
41-
// MongoDBSaver handles this automatically in initializeSchemaVersion
42-
it("should want to apply on empty database", async () => {
43-
// ensure database is empty
44-
const db = client.db(dbName);
45-
await db.dropDatabase();
46-
47-
const migration = new Migration1ObjectMetadata({
48-
client,
49-
dbName,
50-
checkpointCollectionName: "checkpoints",
51-
checkpointWritesCollectionName: "checkpoint_writes",
52-
schemaVersionCollectionName: "schema_version",
53-
serializer: new JsonPlusSerializer(),
54-
currentSchemaVersion: 1,
55-
});
56-
expect(await migration.isApplicable()).toBe(true);
24+
if (!isSkippedCIEnvironment()) {
25+
const dbName = "test_db";
26+
let container: StartedMongoDBContainer;
27+
let client: MongoClient;
28+
29+
beforeAll(async () => {
30+
container = await new MongoDBContainer("mongo:6.0.1").start();
31+
const connectionString = `mongodb://127.0.0.1:${container.getMappedPort(
32+
27017
33+
)}/${dbName}?directConnection=true`;
34+
client = new MongoClient(connectionString);
5735
});
5836

59-
it("should not want to apply on database with schema version of 1", async () => {
60-
const db = client.db(dbName);
61-
await db.dropDatabase();
62-
await db.createCollection("schema_version");
63-
await db.collection("schema_version").insertOne({ version: 1 });
64-
65-
const migration = new Migration1ObjectMetadata({
66-
client,
67-
dbName,
68-
checkpointCollectionName: "checkpoints",
69-
checkpointWritesCollectionName: "checkpoint_writes",
70-
schemaVersionCollectionName: "schema_version",
71-
serializer: new JsonPlusSerializer(),
72-
currentSchemaVersion: 1,
73-
});
74-
expect(await migration.isApplicable()).toBe(false);
37+
afterAll(async () => {
38+
await client.close();
39+
await container.stop();
7540
});
76-
});
77-
78-
describe("apply", () => {
79-
const expectedCheckpoints: Record<
80-
string,
81-
{
82-
parent_checkpoint_id?: string;
83-
checkpoint: Binary;
84-
type: string;
85-
metadata: CheckpointMetadata;
86-
thread_id: string;
87-
checkpoint_ns: string;
88-
checkpoint_id: string;
89-
}
90-
> = {};
91-
92-
beforeEach(async () => {
93-
const serde = new JsonPlusSerializer();
94-
const dropDb = client.db(dbName);
95-
await dropDb.dropDatabase();
96-
const db = client.db(dbName);
97-
await db.createCollection("checkpoints");
98-
await db.createCollection("schema_version");
99-
100-
for (let i = 0; i < 10; i += 1) {
101-
const checkpoint_id = uuid6(-3);
102-
const thread_id = uuid6(-3);
103-
const checkpoint_ns = "";
104-
105-
const checkpoint: Checkpoint = {
106-
v: 1,
107-
id: checkpoint_id,
108-
ts: new Date().toISOString(),
109-
channel_values: {},
110-
channel_versions: {},
111-
versions_seen: {},
112-
pending_sends: [],
113-
};
114-
115-
const metadata: CheckpointMetadata = {
116-
source: "update",
117-
step: -1,
118-
writes: {},
119-
parents: {},
120-
};
121-
122-
const [checkpointType, serializedCheckpoint] =
123-
serde.dumpsTyped(checkpoint);
124-
const serializedMetadata = serde.dumpsTyped(metadata)[1];
125-
126-
await db.collection("checkpoints").insertOne({
127-
type: checkpointType,
128-
checkpoint: serializedCheckpoint,
129-
metadata: serializedMetadata,
130-
thread_id,
131-
checkpoint_ns,
132-
checkpoint_id,
41+
42+
describe("isApplicable", () => {
43+
// MongoDBSaver handles this automatically in initializeSchemaVersion
44+
it("should want to apply on empty database", async () => {
45+
// ensure database is empty
46+
const db = client.db(dbName);
47+
await db.dropDatabase();
48+
49+
const migration = new Migration1ObjectMetadata({
50+
client,
51+
dbName,
52+
checkpointCollectionName: "checkpoints",
53+
checkpointWritesCollectionName: "checkpoint_writes",
54+
schemaVersionCollectionName: "schema_version",
55+
serializer: new JsonPlusSerializer(),
56+
currentSchemaVersion: 1,
13357
});
58+
expect(await migration.isApplicable()).toBe(true);
59+
});
13460

135-
expectedCheckpoints[checkpoint_id] = {
136-
checkpoint: new Binary(serializedCheckpoint),
137-
type: checkpointType,
138-
metadata,
139-
thread_id,
140-
checkpoint_ns,
141-
checkpoint_id,
142-
};
143-
}
61+
it("should not want to apply on database with schema version of 1", async () => {
62+
const db = client.db(dbName);
63+
await db.dropDatabase();
64+
await db.createCollection("schema_version");
65+
await db.collection("schema_version").insertOne({ version: 1 });
66+
67+
const migration = new Migration1ObjectMetadata({
68+
client,
69+
dbName,
70+
checkpointCollectionName: "checkpoints",
71+
checkpointWritesCollectionName: "checkpoint_writes",
72+
schemaVersionCollectionName: "schema_version",
73+
serializer: new JsonPlusSerializer(),
74+
currentSchemaVersion: 1,
75+
});
76+
expect(await migration.isApplicable()).toBe(false);
77+
});
14478
});
14579

146-
it("should migrate all checkpoints", async () => {
147-
const migration = new Migration1ObjectMetadata({
148-
client,
149-
dbName,
150-
checkpointCollectionName: "checkpoints",
151-
checkpointWritesCollectionName: "checkpoint_writes",
152-
schemaVersionCollectionName: "schema_version",
153-
serializer: new JsonPlusSerializer(),
154-
currentSchemaVersion: 1,
80+
describe("apply", () => {
81+
const expectedCheckpoints: Record<
82+
string,
83+
{
84+
parent_checkpoint_id?: string;
85+
checkpoint: Binary;
86+
type: string;
87+
metadata: CheckpointMetadata;
88+
thread_id: string;
89+
checkpoint_ns: string;
90+
checkpoint_id: string;
91+
}
92+
> = {};
93+
94+
beforeEach(async () => {
95+
const serde = new JsonPlusSerializer();
96+
const dropDb = client.db(dbName);
97+
await dropDb.dropDatabase();
98+
const db = client.db(dbName);
99+
await db.createCollection("checkpoints");
100+
await db.createCollection("schema_version");
101+
102+
for (let i = 0; i < 10; i += 1) {
103+
const checkpoint_id = uuid6(-3);
104+
const thread_id = uuid6(-3);
105+
const checkpoint_ns = "";
106+
107+
const checkpoint: Checkpoint = {
108+
v: 1,
109+
id: checkpoint_id,
110+
ts: new Date().toISOString(),
111+
channel_values: {},
112+
channel_versions: {},
113+
versions_seen: {},
114+
pending_sends: [],
115+
};
116+
117+
const metadata: CheckpointMetadata = {
118+
source: "update",
119+
step: -1,
120+
writes: {},
121+
parents: {},
122+
};
123+
124+
const [checkpointType, serializedCheckpoint] =
125+
serde.dumpsTyped(checkpoint);
126+
const serializedMetadata = serde.dumpsTyped(metadata)[1];
127+
128+
await db.collection("checkpoints").insertOne({
129+
type: checkpointType,
130+
checkpoint: serializedCheckpoint,
131+
metadata: serializedMetadata,
132+
thread_id,
133+
checkpoint_ns,
134+
checkpoint_id,
135+
});
136+
137+
expectedCheckpoints[checkpoint_id] = {
138+
checkpoint: new Binary(serializedCheckpoint),
139+
type: checkpointType,
140+
metadata,
141+
thread_id,
142+
checkpoint_ns,
143+
checkpoint_id,
144+
};
145+
}
146+
});
147+
148+
it("should migrate all checkpoints", async () => {
149+
const migration = new Migration1ObjectMetadata({
150+
client,
151+
dbName,
152+
checkpointCollectionName: "checkpoints",
153+
checkpointWritesCollectionName: "checkpoint_writes",
154+
schemaVersionCollectionName: "schema_version",
155+
serializer: new JsonPlusSerializer(),
156+
currentSchemaVersion: 1,
157+
});
158+
await migration.apply();
159+
160+
const db = client.db(dbName);
161+
const cursor = await db.collection("checkpoints").find({});
162+
163+
let docCount = 0;
164+
for await (const actual of cursor) {
165+
docCount += 1;
166+
const expected = expectedCheckpoints[actual.checkpoint_id];
167+
expect(actual.parent_checkpoint_id).toBe(
168+
expected.parent_checkpoint_id
169+
);
170+
expect(actual.type).toBe(expected.type);
171+
expect(actual.checkpoint).toEqual(expected.checkpoint);
172+
expect(actual.metadata).toEqual(expected.metadata);
173+
expect(actual.thread_id).toBe(expected.thread_id);
174+
expect(actual.checkpoint_ns).toBe(expected.checkpoint_ns);
175+
expect(actual.checkpoint_id).toBe(expected.checkpoint_id);
176+
}
177+
expect(docCount).toBe(10);
155178
});
156-
await migration.apply();
157-
158-
const db = client.db(dbName);
159-
const cursor = await db.collection("checkpoints").find({});
160-
161-
let docCount = 0;
162-
for await (const actual of cursor) {
163-
docCount += 1;
164-
const expected = expectedCheckpoints[actual.checkpoint_id];
165-
expect(actual.parent_checkpoint_id).toBe(expected.parent_checkpoint_id);
166-
expect(actual.type).toBe(expected.type);
167-
expect(actual.checkpoint).toEqual(expected.checkpoint);
168-
expect(actual.metadata).toEqual(expected.metadata);
169-
expect(actual.thread_id).toBe(expected.thread_id);
170-
expect(actual.checkpoint_ns).toBe(expected.checkpoint_ns);
171-
expect(actual.checkpoint_id).toBe(expected.checkpoint_id);
172-
}
173-
expect(docCount).toBe(10);
174179
});
175-
});
180+
} else {
181+
it.skip("GitHub can't run containers on M-Series macOS runners due to lack of support for nested virtualization.", () => {});
182+
}
176183
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { platform, arch } from "node:os";
2+
3+
function isMSeriesMac() {
4+
return platform() === "darwin" && arch() === "arm64";
5+
}
6+
7+
function isWindows() {
8+
return platform() === "win32";
9+
}
10+
11+
function isCI() {
12+
// eslint-disable-next-line no-process-env
13+
return (process.env.CI ?? "").toLowerCase() === "true";
14+
}
15+
16+
/**
17+
* GitHub Actions doesn't support containers on m-series macOS due to a lack of hypervisor support for nested
18+
* virtualization.
19+
*
20+
* For details, see https://github.com/actions/runner-images/issues/9460#issuecomment-1981203045
21+
*
22+
* GitHub actions also doesn't support Linux containers on Windows, and may never do so. This is in part due to Docker
23+
* Desktop licensing restrictions, and the complexity of setting up Moby or similar without Docker Desktop.
24+
* Unfortunately, TestContainers doesn't support windows containers, so we can't run the tests on Windows either.
25+
*
26+
* For details, see https://github.com/actions/runner/issues/904 and
27+
* https://java.testcontainers.org/supported_docker_environment/windows/#windows-container-on-windows-wcow
28+
*
29+
*
30+
*/
31+
export function isSkippedCIEnvironment() {
32+
return isCI() && (isWindows() || isMSeriesMac());
33+
}

0 commit comments

Comments
 (0)