From 2063d516e1a1abb25b98a11997e45cc181cc4f8b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:42:57 +0000 Subject: [PATCH 1/2] chore(deps): update dependency @netlify/blobs to v10 --- package-lock.json | 164 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0aaed5a515..7791ac9d24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "devDependencies": { "@fastly/http-compute-js": "1.1.5", - "@netlify/blobs": "^8.2.0", + "@netlify/blobs": "^10.1.0", "@netlify/build": "^35.1.7", "@netlify/config": "^24.0.4", "@netlify/edge-bundler": "^14.5.5", @@ -3764,14 +3764,53 @@ "dev": true }, "node_modules/@netlify/blobs": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-8.2.0.tgz", - "integrity": "sha512-9djLZHBKsoKk8XCgwWSEPK9QnT8qqxEQGuYh48gFIcNLvpBKkLnHbDZuyUxmNemCfDz7h0HnMXgSPnnUVgARhg==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-10.1.0.tgz", + "integrity": "sha512-dFpqDc6/x5LEu9L7kblCQu00CFEchH8J42jmQoXPuhKoE7avajzeLTbVKA8Olk3S/c2m9ejegrgbhL8NRA2Jyw==", "dev": true, + "dependencies": { + "@netlify/dev-utils": "4.3.0", + "@netlify/runtime-utils": "2.2.0" + }, "engines": { "node": "^14.16.0 || >=16.0.0" } }, + "node_modules/@netlify/blobs/node_modules/@netlify/dev-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@netlify/dev-utils/-/dev-utils-4.3.0.tgz", + "integrity": "sha512-vZAL8pMuj3yPQlmHSgyaA/UQFxc6pZgU0LucFJ1+IPWGJtIzBXHRvuR4acpoP72HtyQPUHJ42s7U9GaaSGVNHg==", + "dev": true, + "dependencies": { + "@whatwg-node/server": "^0.10.0", + "ansis": "^4.1.0", + "chokidar": "^4.0.1", + "decache": "^4.6.2", + "dettle": "^1.0.5", + "dot-prop": "9.0.0", + "empathic": "^2.0.0", + "env-paths": "^3.0.0", + "image-size": "^2.0.2", + "js-image-generator": "^1.0.4", + "parse-gitignore": "^2.0.0", + "semver": "^7.7.2", + "tmp-promise": "^3.0.3", + "uuid": "^11.1.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || >=20" + } + }, + "node_modules/@netlify/blobs/node_modules/@netlify/runtime-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@netlify/runtime-utils/-/runtime-utils-2.2.0.tgz", + "integrity": "sha512-K3kWIxIMucibzQsATU2xw2JI+OpS9PZfPW/a+81gmeLC8tLv5YAxTVT0NFY/3imk1kcOJb9g7658jPLqDJaiAw==", + "dev": true, + "engines": { + "node": "^18.14.0 || >=20" + } + }, "node_modules/@netlify/build": { "version": "35.1.7", "resolved": "https://registry.npmjs.org/@netlify/build/-/build-35.1.7.tgz", @@ -3846,45 +3885,6 @@ } } }, - "node_modules/@netlify/build/node_modules/@netlify/blobs": { - "version": "10.0.11", - "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-10.0.11.tgz", - "integrity": "sha512-/pa7eD2gxkhJ6aUIJULrRu3tvAaimy+sA6vHUuGRMvncjOuRpeatXLHxuzdn8DyK1CZCjN3E33oXsdEpoqG7SA==", - "dev": true, - "dependencies": { - "@netlify/dev-utils": "4.2.0", - "@netlify/runtime-utils": "2.1.0" - }, - "engines": { - "node": "^14.16.0 || >=16.0.0" - } - }, - "node_modules/@netlify/build/node_modules/@netlify/dev-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@netlify/dev-utils/-/dev-utils-4.2.0.tgz", - "integrity": "sha512-P/uLJ5IKB4DhUOd6Q4Mpk7N0YKrnijUhAL3C05dEftNi3U3xJB98YekYfsL3G6GkS3L35pKGMx+vKJRwUHpP1Q==", - "dev": true, - "dependencies": { - "@whatwg-node/server": "^0.10.0", - "ansis": "^4.1.0", - "chokidar": "^4.0.1", - "decache": "^4.6.2", - "dettle": "^1.0.5", - "dot-prop": "9.0.0", - "empathic": "^2.0.0", - "env-paths": "^3.0.0", - "image-size": "^2.0.2", - "js-image-generator": "^1.0.4", - "parse-gitignore": "^2.0.0", - "semver": "^7.7.2", - "tmp-promise": "^3.0.3", - "uuid": "^11.1.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || >=20" - } - }, "node_modules/@netlify/cache-utils": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/@netlify/cache-utils/-/cache-utils-6.0.4.tgz", @@ -35126,10 +35126,45 @@ "dev": true }, "@netlify/blobs": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-8.2.0.tgz", - "integrity": "sha512-9djLZHBKsoKk8XCgwWSEPK9QnT8qqxEQGuYh48gFIcNLvpBKkLnHbDZuyUxmNemCfDz7h0HnMXgSPnnUVgARhg==", - "dev": true + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-10.1.0.tgz", + "integrity": "sha512-dFpqDc6/x5LEu9L7kblCQu00CFEchH8J42jmQoXPuhKoE7avajzeLTbVKA8Olk3S/c2m9ejegrgbhL8NRA2Jyw==", + "dev": true, + "requires": { + "@netlify/dev-utils": "4.3.0", + "@netlify/runtime-utils": "2.2.0" + }, + "dependencies": { + "@netlify/dev-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@netlify/dev-utils/-/dev-utils-4.3.0.tgz", + "integrity": "sha512-vZAL8pMuj3yPQlmHSgyaA/UQFxc6pZgU0LucFJ1+IPWGJtIzBXHRvuR4acpoP72HtyQPUHJ42s7U9GaaSGVNHg==", + "dev": true, + "requires": { + "@whatwg-node/server": "^0.10.0", + "ansis": "^4.1.0", + "chokidar": "^4.0.1", + "decache": "^4.6.2", + "dettle": "^1.0.5", + "dot-prop": "9.0.0", + "empathic": "^2.0.0", + "env-paths": "^3.0.0", + "image-size": "^2.0.2", + "js-image-generator": "^1.0.4", + "parse-gitignore": "^2.0.0", + "semver": "^7.7.2", + "tmp-promise": "^3.0.3", + "uuid": "^11.1.0", + "write-file-atomic": "^5.0.1" + } + }, + "@netlify/runtime-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@netlify/runtime-utils/-/runtime-utils-2.2.0.tgz", + "integrity": "sha512-K3kWIxIMucibzQsATU2xw2JI+OpS9PZfPW/a+81gmeLC8tLv5YAxTVT0NFY/3imk1kcOJb9g7658jPLqDJaiAw==", + "dev": true + } + } }, "@netlify/build": { "version": "35.1.7", @@ -35188,41 +35223,6 @@ "yaml": "^2.8.0", "yargs": "^17.6.0", "zod": "^3.25.76" - }, - "dependencies": { - "@netlify/blobs": { - "version": "10.0.11", - "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-10.0.11.tgz", - "integrity": "sha512-/pa7eD2gxkhJ6aUIJULrRu3tvAaimy+sA6vHUuGRMvncjOuRpeatXLHxuzdn8DyK1CZCjN3E33oXsdEpoqG7SA==", - "dev": true, - "requires": { - "@netlify/dev-utils": "4.2.0", - "@netlify/runtime-utils": "2.1.0" - } - }, - "@netlify/dev-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@netlify/dev-utils/-/dev-utils-4.2.0.tgz", - "integrity": "sha512-P/uLJ5IKB4DhUOd6Q4Mpk7N0YKrnijUhAL3C05dEftNi3U3xJB98YekYfsL3G6GkS3L35pKGMx+vKJRwUHpP1Q==", - "dev": true, - "requires": { - "@whatwg-node/server": "^0.10.0", - "ansis": "^4.1.0", - "chokidar": "^4.0.1", - "decache": "^4.6.2", - "dettle": "^1.0.5", - "dot-prop": "9.0.0", - "empathic": "^2.0.0", - "env-paths": "^3.0.0", - "image-size": "^2.0.2", - "js-image-generator": "^1.0.4", - "parse-gitignore": "^2.0.0", - "semver": "^7.7.2", - "tmp-promise": "^3.0.3", - "uuid": "^11.1.0", - "write-file-atomic": "^5.0.1" - } - } } }, "@netlify/cache-utils": { diff --git a/package.json b/package.json index 9402203de6..d9138b02e4 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "homepage": "https://opennext.js.org/netlify", "devDependencies": { "@fastly/http-compute-js": "1.1.5", - "@netlify/blobs": "^8.2.0", + "@netlify/blobs": "^10.1.0", "@netlify/build": "^35.1.7", "@netlify/config": "^24.0.4", "@netlify/edge-bundler": "^14.5.5", From f853fc16cdd87e491628b84b690d0caa61433232 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Mon, 20 Oct 2025 16:26:41 -0400 Subject: [PATCH 2/2] test: update mock interface in getBlobServerGets --- tests/integration/cache-handler.test.ts | 32 +++++++++++------------ tests/integration/fetch-handler.test.ts | 2 +- tests/integration/request-context.test.ts | 4 +-- tests/integration/revalidate-path.test.ts | 12 ++++----- tests/integration/revalidate-tags.test.ts | 14 +++++----- tests/utils/contexts.ts | 6 ++--- tests/utils/helpers.ts | 12 ++++----- 7 files changed, 41 insertions(+), 41 deletions(-) diff --git a/tests/integration/cache-handler.test.ts b/tests/integration/cache-handler.test.ts index b459d62559..23e27101ca 100644 --- a/tests/integration/cache-handler.test.ts +++ b/tests/integration/cache-handler.test.ts @@ -82,7 +82,7 @@ describe('page router', () => { countOfBlobServerGetsForKey(ctx, '/static/revalidate-automatic'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // wait to have page regenerated in the background await new Promise((resolve) => setTimeout(resolve, 1000)) @@ -116,7 +116,7 @@ describe('page router', () => { countOfBlobServerGetsForKey(ctx, '/static/revalidate-automatic'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // ping that should serve the stale page for static/revalidate-slow, while revalidating in background await invokeFunction(ctx, { url: 'static/revalidate-slow' }) @@ -125,7 +125,7 @@ describe('page router', () => { countOfBlobServerGetsForKey(ctx, '/static/revalidate-slow'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // wait to have a stale page await new Promise((resolve) => setTimeout(resolve, 6_000)) @@ -136,7 +136,7 @@ describe('page router', () => { countOfBlobServerGetsForKey(ctx, '/static/revalidate-slow'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // over 5 seconds since it was regenerated, so we should get stale response, // while fresh is generated in the background @@ -160,7 +160,7 @@ describe('page router', () => { countOfBlobServerGetsForKey(ctx, '/static/revalidate-automatic'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // Slow revalidate should still be a hit, but the maxage should be updated const callLater2 = await invokeFunction(ctx, { url: 'static/revalidate-slow' }) @@ -177,7 +177,7 @@ describe('page router', () => { countOfBlobServerGetsForKey(ctx, '/static/revalidate-slow'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // it does not wait for the cache.set so we have to manually wait here until the blob storage got populated await new Promise((resolve) => setTimeout(resolve, 1000)) @@ -204,7 +204,7 @@ describe('page router', () => { countOfBlobServerGetsForKey(ctx, '/static/revalidate-automatic'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() }) }) @@ -245,7 +245,7 @@ describe('app router', () => { countOfBlobServerGetsForKey(ctx, '/posts/1'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // test a prerendered page without TTL const post2 = await invokeFunction(ctx, { url: '/' }) @@ -263,7 +263,7 @@ describe('app router', () => { countOfBlobServerGetsForKey(ctx, '/index'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() expect(await ctx.blobStore.get(encodeBlobKey('/posts/3'))).toBeNull() // this page is not pre-rendered and should result in a cache miss @@ -293,7 +293,7 @@ describe('app router', () => { countOfBlobServerGetsForKey(ctx, '/posts/1'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // it should've been regenerated in the background after the first call // so the date should be different expect(staleDate, 'the date was cached and is matching the initial one').not.toBe(post1Date) @@ -320,7 +320,7 @@ describe('app router', () => { countOfBlobServerGetsForKey(ctx, '/posts/1'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() }) test("not-prerendered pages should be permanently cached when produced by sandboxed invocations that don't share memory", async (ctx) => { @@ -405,7 +405,7 @@ describe('route', () => { }) expect(blobEntry).not.toBeNull() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // test the first invocation of the route - we should get stale response while fresh is generated in the background const call1 = await invokeFunction(ctx, { url: '/api/revalidate-handler' }) @@ -428,7 +428,7 @@ describe('route', () => { countOfBlobServerGetsForKey(ctx, '/api/revalidate-handler'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // it does not wait for the cache.set so we have to manually wait here until the blob storage got populated await new Promise((resolve) => setTimeout(resolve, 1000)) @@ -458,7 +458,7 @@ describe('route', () => { countOfBlobServerGetsForKey(ctx, '/api/revalidate-handler'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // wait to have a stale route again await new Promise((resolve) => setTimeout(resolve, 8_000)) @@ -485,7 +485,7 @@ describe('route', () => { countOfBlobServerGetsForKey(ctx, '/api/revalidate-handler'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // it does not wait for the cache.set so we have to manually wait here until the blob storage got populated await new Promise((resolve) => setTimeout(resolve, 1000)) @@ -510,7 +510,7 @@ describe('route', () => { countOfBlobServerGetsForKey(ctx, '/api/revalidate-handler'), 'should only try to get value once from blob store (date calculation should not trigger additional blobs.get)', ).toBe(1) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() }) test('cacheable route handler response not produced at build is served correctly', async (ctx) => { diff --git a/tests/integration/fetch-handler.test.ts b/tests/integration/fetch-handler.test.ts index c90bf44af3..3901bf0997 100644 --- a/tests/integration/fetch-handler.test.ts +++ b/tests/integration/fetch-handler.test.ts @@ -363,7 +363,7 @@ test('does not fetch same cached fetch data from blobs twice 'expected blobs for all types of values to be retrieved at most once per key (including fetch data, tag manifests, static files)', ).toBeDistinct() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() handlerCalled = 0 const request2 = await invokeFunction(ctx, { url: 'same-fetch-multiple-times/99', diff --git a/tests/integration/request-context.test.ts b/tests/integration/request-context.test.ts index 31788cdf91..9b9efe0cb6 100644 --- a/tests/integration/request-context.test.ts +++ b/tests/integration/request-context.test.ts @@ -106,7 +106,7 @@ describe('request-context does NOT leak between concurrent requests', () => { lastModified: new Date(mockedDateForRevalidateSlow).getTime(), }) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const waitForCacheHandlerGetAndPausePromise = waitForCacheHandlerGetAndPause('/static/revalidate-slow') @@ -182,7 +182,7 @@ describe('request-context does NOT leak between concurrent requests', () => { lastModified: new Date(mockedDateForStaticFetch2).getTime(), }) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const waitForCacheHandlerGetAndPausePromise = waitForCacheHandlerGetAndPause('/static-fetch/2') const slowCallPromise = invokeFunction(ctx, { diff --git a/tests/integration/revalidate-path.test.ts b/tests/integration/revalidate-path.test.ts index f326eea0b6..ed7d9a4f66 100644 --- a/tests/integration/revalidate-path.test.ts +++ b/tests/integration/revalidate-path.test.ts @@ -58,7 +58,7 @@ test('should revalidate a route by path', async (ctx) => { expect(await ctx.blobStore.get(encodeBlobKey('/static-fetch/1'))).not.toBeNull() expect(await ctx.blobStore.get(encodeBlobKey('_N_T_/static-fetch/[id]/page'))).toBeNull() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // test the function call const post1 = await invokeFunction(ctx, { url: '/static-fetch/1' }) @@ -78,7 +78,7 @@ test('should revalidate a route by path', async (ctx) => { getBlobServerGets(ctx, isTagManifest), `expected tag manifests to be retrieved at most once per tag`, ).toBeDistinct() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const post1Route2 = await invokeFunction(ctx, { url: '/static-fetch/2' }) expect(post1Route2.statusCode).toBe(200) @@ -95,7 +95,7 @@ test('should revalidate a route by path', async (ctx) => { getBlobServerGets(ctx, isTagManifest), `expected tag manifests to be retrieved at most once per tag`, ).toBeDistinct() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const revalidate = await invokeFunction(ctx, { url: '/api/on-demand-revalidate/path' }) expect(revalidate.statusCode).toBe(200) @@ -106,7 +106,7 @@ test('should revalidate a route by path', async (ctx) => { expect(await ctx.blobStore.get(encodeBlobKey('_N_T_/static-fetch/[id]/page'))).not.toBeNull() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const post2 = await invokeFunction(ctx, { url: '/static-fetch/1' }) const post2Date = load(post2.body)('[data-testid="date-now"]').text() @@ -126,7 +126,7 @@ test('should revalidate a route by path', async (ctx) => { getBlobServerGets(ctx, isTagManifest), `expected tag manifests to be retrieved at most once per tag`, ).toBeDistinct() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const post2Route2 = await invokeFunction(ctx, { url: '/static-fetch/2' }) @@ -144,5 +144,5 @@ test('should revalidate a route by path', async (ctx) => { getBlobServerGets(ctx, isTagManifest), `expected tag manifests to be retrieved at most once per tag`, ).toBeDistinct() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() }) diff --git a/tests/integration/revalidate-tags.test.ts b/tests/integration/revalidate-tags.test.ts index c93f45a980..87316cbda8 100644 --- a/tests/integration/revalidate-tags.test.ts +++ b/tests/integration/revalidate-tags.test.ts @@ -57,7 +57,7 @@ test('should revalidate a route by tag', async (ctx) => { expect(await ctx.blobStore.get(encodeBlobKey('/static-fetch-1'))).not.toBeNull() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // test the function call const post1 = await invokeFunction(ctx, { url: '/static-fetch-1' }) @@ -78,7 +78,7 @@ test('should revalidate a route by tag', async (ctx) => { getBlobServerGets(ctx, isTagManifest), `expected tag manifests to be retrieved at most once per tag`, ).toBeDistinct() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const revalidate = await invokeFunction(ctx, { url: '/api/on-demand-revalidate/tag' }) expect(revalidate.statusCode).toBe(200) @@ -87,7 +87,7 @@ test('should revalidate a route by tag', async (ctx) => { // it does not wait for the revalidation await new Promise((resolve) => setTimeout(resolve, 100)) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const post2 = await invokeFunction(ctx, { url: '/static-fetch-1' }) const post2Date = load(post2.body)('[data-testid="date-now"]').text() @@ -109,7 +109,7 @@ test('should revalidate a route by tag', async (ctx) => { getBlobServerGets(ctx, isTagManifest), `expected tag manifests to be retrieved at most once per tag`, ).toBeDistinct() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() // it does not wait for the cache.set so we have to manually wait here until the blob storage got populated await new Promise((resolve) => setTimeout(resolve, 100)) @@ -134,7 +134,7 @@ test('should revalidate a route by tag', async (ctx) => { getBlobServerGets(ctx, isTagManifest), `expected tag manifests to be retrieved at most once per tag`, ).toBeDistinct() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const revalidate2 = await invokeFunction(ctx, { url: '/api/on-demand-revalidate/tag' }) expect(revalidate2.statusCode).toBe(200) @@ -143,7 +143,7 @@ test('should revalidate a route by tag', async (ctx) => { // it does not wait for the revalidation await new Promise((resolve) => setTimeout(resolve, 100)) - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() const post4 = await invokeFunction(ctx, { url: '/static-fetch-1' }) const post4Date = load(post4.body)('[data-testid="date-now"]').text() @@ -165,5 +165,5 @@ test('should revalidate a route by tag', async (ctx) => { getBlobServerGets(ctx, isTagManifest), `expected tag manifests to be retrieved at most once per tag`, ).toBeDistinct() - ctx.blobServerGetSpy.mockClear() + ctx.blobServerOnRequestSpy.mockClear() }) diff --git a/tests/utils/contexts.ts b/tests/utils/contexts.ts index 874dc8d2b6..bc2bebe99f 100644 --- a/tests/utils/contexts.ts +++ b/tests/utils/contexts.ts @@ -1,7 +1,7 @@ -import { type getStore } from '@netlify/blobs' +import type { getStore } from '@netlify/blobs' import { BlobsServer } from '@netlify/blobs/server' import { type WriteStream } from 'node:fs' -import { MockInstance, TestContext } from 'vitest' +import { TestContext } from 'vitest' export interface FixtureTestContext extends TestContext { cwd: string @@ -10,7 +10,7 @@ export interface FixtureTestContext extends TestContext { blobStoreHost: string blobStorePort: number blobServer: BlobsServer - blobServerGetSpy: MockInstance, ReturnType> + blobServerOnRequestSpy: BlobsServer['onRequest'] blobStore: ReturnType functionDist: string edgeFunctionPort: number diff --git a/tests/utils/helpers.ts b/tests/utils/helpers.ts index b540aea7c3..99f741fb6e 100644 --- a/tests/utils/helpers.ts +++ b/tests/utils/helpers.ts @@ -1,7 +1,7 @@ import getPort from 'get-port' import { getDeployStore } from '@netlify/blobs' -import { BlobsServer } from '@netlify/blobs/server' +import { BlobsServer, Operation } from '@netlify/blobs/server' import type { NetlifyPluginUtils } from '@netlify/build' import { Buffer } from 'node:buffer' import { mkdtemp } from 'node:fs/promises' @@ -34,13 +34,14 @@ export const generateRandomObjectID = () => { export const startMockBlobStore = async (ctx: FixtureTestContext) => { const port = await getPort() // create new blob store server + ctx.blobServerOnRequestSpy = vi.fn() ctx.blobServer = new BlobsServer({ port, token: BLOB_TOKEN, + onRequest: ctx.blobServerOnRequestSpy, directory: await mkdtemp(join(tmpdir(), 'opennextjs-netlify-blob-')), }) await ctx.blobServer.start() - ctx.blobServerGetSpy = vi.spyOn(ctx.blobServer, 'get') ctx.blobStoreHost = `localhost:${port}` ctx.blobStorePort = port vi.stubEnv('NETLIFY_BLOBS_CONTEXT', createBlobContext(ctx)) @@ -72,11 +73,10 @@ export const getBlobEntries = async (ctx: FixtureTestContext) => { export function getBlobServerGets(ctx: FixtureTestContext, predicate?: (key: string) => boolean) { const isString = (arg: unknown): arg is string => typeof arg === 'string' - return ctx.blobServerGetSpy.mock.calls + return ctx.blobServerOnRequestSpy.mock.calls .map(([request]) => { - if (typeof request.url !== 'string') { - return undefined - } + if (request.type !== Operation.GET) return undefined + if (!isString(request.url)) return undefined let urlSegments = request.url.split('/').slice(1)