-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Upgrade to vitest2 #530
Upgrade to vitest2 #530
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--- | ||
--- |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ import { promiseStateAsync as pStateAsync } from "p-state"; | |
import type { Mock, MockInstance } from "vitest"; | ||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; | ||
import type { MinimalClient } from "../MinimalClientContext.js"; | ||
import type { AsyncClientCache } from "./Cache.js"; | ||
import type { AsyncClientCache, AsyncFactory } from "./Cache.js"; | ||
import { createAsyncClientCache, createClientCache } from "./Cache.js"; | ||
|
||
declare module "vitest" { | ||
|
@@ -89,20 +89,14 @@ describe("AsyncCache", () => { | |
}); | ||
|
||
describe("race checks", () => { | ||
let factoryFn: Mock<[MinimalClient, string], Promise<any>>; | ||
let factoryFn: Mock<AsyncFactory<any, any>>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honestly, this is a way better type api for vitest (taking the typeof a function instead of an array of args and a return value). Alas, it required minor changes in this file |
||
let cache: ReturnType<typeof createSpys>; | ||
let inProgress: ReturnType<typeof createSpys>; | ||
let asyncCache: AsyncClientCache<string, string>; | ||
let asyncCacheSpies: { | ||
[K in keyof typeof asyncCache]: MockInstance< | ||
Parameters<typeof asyncCache[K]>, | ||
ReturnType<typeof asyncCache[K]> | ||
>; | ||
[K in keyof typeof asyncCache]: MockInstance<typeof asyncCache[K]>; | ||
}; | ||
let asyncSetSpy: MockInstance< | ||
[client: MinimalClient, key: string, value: string | Promise<string>], | ||
Promise<any> | ||
>; | ||
let asyncSetSpy: MockInstance<typeof asyncCache.set>; | ||
let factoryDefers: DeferredPromise<string>[]; | ||
let getPromises: Promise<string>[]; | ||
|
||
|
@@ -114,7 +108,7 @@ describe("AsyncCache", () => { | |
let expectedPending: Record<string, number> = {}; | ||
|
||
beforeEach(async () => { | ||
factoryFn = vi.fn<any, Promise<any>>(); | ||
factoryFn = vi.fn(); | ||
factoryFn.mockImplementation(() => { | ||
const defer = pDefer<string>(); | ||
factoryDefers.push(defer); | ||
|
@@ -274,10 +268,10 @@ describe("AsyncCache", () => { | |
i++ | ||
) { | ||
if (asyncCacheSpies.get.mock.calls[i][1] === key) { | ||
expect(asyncCacheSpies.get.mock.results[i].type).toBe( | ||
"throw", | ||
expect(asyncCacheSpies.get.mock.settledResults[i].type).toBe( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vitest in 1.x was very very strange wrt to how it handled functions that returned promises. In the old way, if the returned promise was resolved/rejected, the value itself was treated like the return value from the function. Otherwise it was treated as pending. Of course a function that returns a Promise could throw unrelated to that promise and you might want to test for that. So they changed the API to be far far more logical in terms of whats happening in the underlying system but that also broke all these carefully crafted tests.
|
||
"rejected", | ||
); | ||
expect(asyncCacheSpies.get.mock.results[i].value) | ||
expect(asyncCacheSpies.get.mock.settledResults[i].value) | ||
.toMatchInlineSnapshot(`[Error: aError]`); | ||
} | ||
} | ||
|
@@ -352,7 +346,7 @@ describe("AsyncCache", () => { | |
|
||
function itReturnsForAsyncGet(results: any[]) { | ||
it("returns for async get", () => { | ||
expect(asyncCacheSpies.get.mock.results.map(a => a.value)) | ||
expect(asyncCacheSpies.get.mock.settledResults.map(a => a.value)) | ||
.toEqual(results); | ||
}); | ||
} | ||
|
@@ -396,8 +390,8 @@ describe("AsyncCache", () => { | |
]); | ||
|
||
itReturnsForAsyncGet([ | ||
expect.any(Promise), | ||
expect.any(Promise), | ||
undefined, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before we could get an object that appeared to be a Promise from |
||
undefined, | ||
"bResult", | ||
]); | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -419,7 +419,7 @@ function createMockWebSocketConstructor( | |
eventEmitter.removeEventListener.bind(eventEmitter), | ||
) as any, | ||
|
||
send: vi.fn((a, _b) => { | ||
send: vi.fn((a, _b: any) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why'd you need to add a type for _b? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in the old way of the type being args and return value, there was only one way to resolve the function. But now that its |
||
logger.debug( | ||
{ message: JSON.parse(a.toString()), webSocketInst }, | ||
"send() called", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2023 Palantir Technologies, Inc. All rights reserved. | ||
* Copyright 2024 Palantir Technologies, Inc. All rights reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
|
@@ -14,21 +14,31 @@ | |
* limitations under the License. | ||
*/ | ||
|
||
// @ts-check | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file is the old test setup file from |
||
import { __testSeamOnly_NotSemverStable__GeneratePackageCommand as GeneratePackageCommand } from "@osdk/foundry-sdk-generator"; | ||
import { apiServer } from "@osdk/shared.test"; | ||
import { rm } from "fs/promises"; | ||
import { join } from "path"; | ||
import { GeneratePackageCommand } from "../generate/index.js"; | ||
import * as fs from "node:fs/promises"; | ||
import * as path from "node:path"; | ||
import { fileURLToPath } from "node:url"; | ||
|
||
const dir = `${__dirname}/../generatedNoCheck/`; | ||
export async function setup() { | ||
const dir = path.join( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ESM doesn't have |
||
path.dirname(fileURLToPath(import.meta.url)), | ||
"src", | ||
"generatedNoCheck", | ||
); | ||
async function setup() { | ||
apiServer.listen(); | ||
|
||
try { | ||
await rm(join(dir, "@test-app"), { recursive: true }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the old code, this could throw causing the second rm to not run. |
||
await rm(join(dir, "@test-app2"), { recursive: true }); | ||
} catch (e) { | ||
// Only needed for regenerations | ||
} | ||
const testAppDir = path.join(dir, "@test-app"); | ||
const testApp2Dir = path.join(dir, "@test-app2"); | ||
|
||
await rmRf(testAppDir); | ||
await rmRf(testApp2Dir); | ||
|
||
await safeStat(testAppDir, "should not exist"); | ||
await safeStat(testApp2Dir, "should not exist"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After upgrading to vitest 2, on initial runs of this test suite, everything would fail due to not being able to find the generated code. And then later when I was testing to find out what was happening I realized that if you bailed on Turns out that in vitest 1, the global setup would run before any forks for the test runners would happen but in vitest 2, the forks happen right away. There also seemed to be some caching of the filesystem on initial load such that no matter what, on the first run, even though I could confirm we fully regenerated prior to the test running, the test would still think those files didn't exist. I figured there was no harm in keeping the extra sanity checks after I chased the behavior down, so it remains. |
||
|
||
await fs.mkdir(dir, { recursive: true }); | ||
|
||
const generatePackageCommand = new GeneratePackageCommand(); | ||
await generatePackageCommand.handler({ | ||
|
@@ -69,6 +79,8 @@ export async function setup() { | |
$0: "", | ||
}); | ||
|
||
await safeStat(testAppDir, "should exist"); | ||
|
||
await generatePackageCommand.handler({ | ||
packageName: "@test-app2/osdk", | ||
packageVersion: "0.0.1", | ||
|
@@ -107,10 +119,54 @@ export async function setup() { | |
_: [], | ||
$0: "", | ||
}); | ||
|
||
await safeStat(testApp2Dir, "should exist"); | ||
} | ||
|
||
export async function teardown() { | ||
// eslint-disable-next-line no-console | ||
console.log("Test teardown: stopping API server"); | ||
console.log("teardown: stopping API server"); | ||
apiServer.close(); | ||
} | ||
|
||
await setup(); | ||
await teardown(); | ||
|
||
/** | ||
* @param {string} testAppDir | ||
*/ | ||
async function rmRf(testAppDir) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Split the deletion out so its more readable up above and so that the first failure doesn't cause the be ignored. |
||
try { | ||
await fs.rm(testAppDir, { recursive: true }); | ||
} catch (e) { | ||
// eslint-disable-next-line no-console | ||
console.debug("rm error", e); | ||
// Only needed for regenerations | ||
} | ||
} | ||
|
||
/** | ||
* @param {string} filePath | ||
* @param {"should exist" | "should not exist"} type | ||
* @returns | ||
*/ | ||
async function safeStat(filePath, type) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
try { | ||
const ret = await fs.stat(filePath); | ||
if (type !== "should exist") { | ||
throw new Error(`Expected ${filePath} to not exist`); | ||
} | ||
|
||
// eslint-disable-next-line no-console | ||
console.log(`safeStat: ${filePath} exists`); | ||
return ret; | ||
} catch (e) { | ||
if (type === "should exist") { | ||
throw new Error(`Expected ${filePath} to exist`); | ||
} | ||
|
||
// eslint-disable-next-line no-console | ||
console.log(`safeStat: ${filePath} does not exist`); | ||
return undefined; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,34 +9,35 @@ | |
}, | ||
"exports": { | ||
".": { | ||
"require": "./build/cjs/index.cjs", | ||
"browser": "./build/browser/index.js", | ||
"import": "./build/esm/index.js" | ||
}, | ||
"./*": { | ||
"require": "./build/cjs/public/*.cjs", | ||
"browser": "./build/browser/public/*.js", | ||
"import": "./build/esm/public/*.js" | ||
} | ||
}, | ||
"scripts": { | ||
"check-attw": "monorepo.tool.attw both", | ||
"check-attw": "monorepo.tool.attw esm", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was a |
||
"check-spelling": "cspell --quiet .", | ||
"clean": "rm -rf lib dist types build tsconfig.tsbuildinfo", | ||
"codegen": "node ./generateMockOntology.js", | ||
"fix-lint": "eslint . --fix && dprint fmt --config $(find-up dprint.json)", | ||
"lint": "eslint . && dprint check --config $(find-up dprint.json)", | ||
"test": "vitest run --pool=forks", | ||
"transpile": "monorepo.tool.transpile", | ||
"typecheck": "monorepo.tool.typecheck both" | ||
"typecheck": "monorepo.tool.typecheck esm" | ||
}, | ||
"dependencies": { | ||
"@osdk/foundry-sdk-generator": "workspace:~", | ||
"execa": "^9.3.0" | ||
}, | ||
"devDependencies": { | ||
"@osdk/legacy-client": "workspace:~", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we execute some of the generated code we need to have access to its dependencies in this project. |
||
"@osdk/monorepo.api-extractor": "workspace:~", | ||
"@osdk/monorepo.tsconfig": "workspace:~", | ||
"@osdk/monorepo.tsup": "workspace:~", | ||
"@osdk/shared.test": "workspace:~", | ||
"typescript": "^5.5.4" | ||
}, | ||
"publishConfig": { | ||
|
@@ -53,6 +54,6 @@ | |
], | ||
"main": "./build/cjs/index.cjs", | ||
"module": "./build/esm/index.js", | ||
"types": "./build/cjs/index.d.cts", | ||
"types": "./build/esm/index.d.ts", | ||
"type": "module" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just preventing future annoying behavior. Typescript doesn't actually check the .mjs files so everything was working here with any string but we were getting red in the IDE. This will at least prevent that mismatch.