diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index 1d84deb1..149becd3 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -931,18 +931,21 @@ workflows: branches: only: requires: + - nuget-hold - build-bindings-backend-linux - build-csharp-windows: filters: branches: only: requires: - - build-bindings-backend-windows + - nuget-hold + - build-bindings-backend-windows - build-csharp-macos: filters: branches: only: requires: + - nuget-hold - build-bindings-backend-macos - store-and-upload-nupkgs: filters: diff --git a/gpt4all-bindings/typescript/src/util.js b/gpt4all-bindings/typescript/src/util.js index dc2e5c14..e854d9eb 100644 --- a/gpt4all-bindings/typescript/src/util.js +++ b/gpt4all-bindings/typescript/src/util.js @@ -105,7 +105,7 @@ function downloadModel(modelName, options = {}) { verbose: false, ...options, }; - + const modelFileName = appendBinSuffixIfMissing(modelName); const partialModelPath = path.join( downloadOptions.modelPath, @@ -115,6 +115,8 @@ function downloadModel(modelName, options = {}) { const modelUrl = downloadOptions.url ?? `https://gpt4all.io/models/${modelFileName}`; + mkdirp.sync(downloadOptions.modelPath) + if (existsSync(finalModelPath)) { throw Error(`Model already exists at ${finalModelPath}`); } diff --git a/gpt4all-bindings/typescript/test/gpt4all.test.js b/gpt4all-bindings/typescript/test/gpt4all.test.js index 0456932a..ed4ddbb7 100644 --- a/gpt4all-bindings/typescript/test/gpt4all.test.js +++ b/gpt4all-bindings/typescript/test/gpt4all.test.js @@ -1,6 +1,7 @@ const path = require("node:path"); const os = require("node:os"); const fsp = require("node:fs/promises"); +const { existsSync } = require('node:fs'); const { LLModel } = require("node-gyp-build")(path.resolve(__dirname, "..")); const { listModels, @@ -19,6 +20,7 @@ const { createCompletion, } = require("../src/gpt4all.js"); const { mock } = require("node:test"); +const { mkdirp } = require("mkdirp"); describe("config", () => { test("default paths constants are available and correct", () => { @@ -116,7 +118,7 @@ describe("downloadModel", () => { return mockFetchImplementation; }; - beforeEach(() => { + beforeEach(async () => { // Mocking the AbortController constructor mockAbortController = jest.fn(); global.AbortController = mockAbortController; @@ -126,19 +128,35 @@ describe("downloadModel", () => { }); mockFetch = createMockFetch(); jest.spyOn(global, "fetch").mockImplementation(mockFetch); + }); - afterEach(() => { + afterEach(async () => { // Clean up mocks mockAbortController.mockReset(); mockFetch.mockClear(); global.fetch.mockRestore(); + + const rootDefaultPath = path.resolve(DEFAULT_DIRECTORY), + partialPath = path.resolve(rootDefaultPath, fakeModelName+'.part'), + fullPath = path.resolve(rootDefaultPath, fakeModelName+'.bin') + + //if tests fail, remove the created files + // acts as cleanup if tests fail + // + if(existsSync(fullPath)) { + await fsp.rm(fullPath) + } + if(existsSync(partialPath)) { + await fsp.rm(partialPath) + } + }); test("should successfully download a model file", async () => { const downloadController = downloadModel(fakeModelName); const modelFilePath = await downloadController.promise; - expect(modelFilePath).toBe(`${DEFAULT_DIRECTORY}/${fakeModelName}.bin`); + expect(modelFilePath).toBe(path.resolve(DEFAULT_DIRECTORY, `${fakeModelName}.bin`)); expect(global.fetch).toHaveBeenCalledTimes(1); expect(global.fetch).toHaveBeenCalledWith( @@ -153,7 +171,7 @@ describe("downloadModel", () => { ); // final model file should be present - expect(fsp.access(modelFilePath)).resolves.not.toThrow(); + await expect(fsp.access(modelFilePath)).resolves.not.toThrow(); // remove the testing model file await fsp.unlink(modelFilePath); @@ -165,17 +183,17 @@ describe("downloadModel", () => { }); // the promise should reject with a mismatch await expect(downloadController.promise).rejects.toThrow( - `Model "${fakeModelName}" failed verification: Hashes mismatch.` + `Model "fake-model" failed verification: Hashes mismatch. Expected wrong-md5sum, got 08d6c05a21512a79a1dfeb9d2a8f262f` ); // fetch should have been called expect(global.fetch).toHaveBeenCalledTimes(1); // the file should be missing - expect( - fsp.access(`${DEFAULT_DIRECTORY}/${fakeModelName}.bin`) + await expect( + fsp.access(path.resolve(DEFAULT_DIRECTORY, `${fakeModelName}.bin`)) ).rejects.toThrow(); // partial file should also be missing - expect( - fsp.access(`${DEFAULT_DIRECTORY}/${fakeModelName}.part`) + await expect( + fsp.access(path.resolve(DEFAULT_DIRECTORY, `${fakeModelName}.part`)) ).rejects.toThrow(); });