From dbbc05e5f025abb819179b155d3162adfcfda036 Mon Sep 17 00:00:00 2001 From: Himself65 Date: Tue, 28 Mar 2023 12:42:40 -0500 Subject: [PATCH] test: improve coverage on affine api (#1727) --- .github/actions/setup-node/action.yml | 6 + .github/workflows/build.yml | 6 - apps/web/src/plugins/affine/index.tsx | 6 +- package.json | 1 + .../src/affine/__tests__/api.spec.ts | 126 +++++++++++++----- packages/workspace/src/affine/api/index.ts | 14 +- yarn.lock | 8 ++ 7 files changed, 119 insertions(+), 48 deletions(-) diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index 684975bc73..e1e11e7031 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -52,6 +52,12 @@ runs: restore-keys: | yarn-download-cache- + - name: Restore node_modules cache + uses: actions/cache@v3 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + - name: Restore yarn install state id: yarn-install-state-cache uses: actions/cache@v3 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f343b01243..e3645372a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,7 +96,6 @@ jobs: name: Unit Test runs-on: ubuntu-latest environment: development - needs: [build] services: octobase: image: ghcr.io/toeverything/cloud-self-hosted:nightly-latest @@ -111,11 +110,6 @@ jobs: - uses: actions/checkout@v3 - name: Setup Node.js uses: ./.github/actions/setup-node - - name: Download artifact - uses: actions/download-artifact@v3 - with: - name: artifact - path: ./apps/web/.next - name: Unit Test run: yarn run test:unit:coverage diff --git a/apps/web/src/plugins/affine/index.tsx b/apps/web/src/plugins/affine/index.tsx index c0facda071..f135f2aa4f 100644 --- a/apps/web/src/plugins/affine/index.tsx +++ b/apps/web/src/plugins/affine/index.tsx @@ -80,7 +80,11 @@ export const AffinePlugin: WorkspacePlugin = { const url = await blobs.get(id); if (url) { const blob = await fetch(url).then(res => res.blob()); - await affineApis.uploadBlob(newWorkspaceId, blob); + await affineApis.uploadBlob( + newWorkspaceId, + await blob.arrayBuffer(), + blob.type + ); } } } diff --git a/package.json b/package.json index 057ca0148f..01a547b6f3 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ }, "devDependencies": { "@affine/cli": "workspace:*", + "@faker-js/faker": "^7.6.0", "@istanbuljs/schema": "^0.1.3", "@perfsee/sdk": "^1.5.1", "@playwright/test": "^1.32.1", diff --git a/packages/workspace/src/affine/__tests__/api.spec.ts b/packages/workspace/src/affine/__tests__/api.spec.ts index 56ebf48550..e050fec750 100644 --- a/packages/workspace/src/affine/__tests__/api.spec.ts +++ b/packages/workspace/src/affine/__tests__/api.spec.ts @@ -3,10 +3,12 @@ */ import 'fake-indexeddb/auto'; +import { readFile } from 'node:fs/promises'; + import { MessageCode } from '@affine/env/constant'; -import userA from '@affine-test/fixtures/userA.json'; import { assertExists } from '@blocksuite/global/utils'; import { Workspace } from '@blocksuite/store'; +import { faker } from '@faker-js/faker'; import { beforeEach, describe, expect, test, vi } from 'vitest'; import { @@ -24,40 +26,36 @@ import { let workspaceApis: ReturnType; let affineAuth: ReturnType; +const mockUser = { + name: faker.name.fullName(), + email: faker.internet.email(), + password: faker.internet.password(), +}; + +beforeEach(() => { + // create a new user for each test, so that each test can be run independently + mockUser.name = faker.name.fullName(); + mockUser.email = faker.internet.email(); + mockUser.password = faker.internet.password(); +}); + beforeEach(() => { affineAuth = createAffineAuth('http://localhost:3000/'); workspaceApis = createWorkspaceApis('http://localhost:3000/'); }); beforeEach(async () => { - let data; - // first step: try to log in - const response = await fetch('http://localhost:3000/api/user/token', { + const data = await fetch('http://localhost:3000/api/user/token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ - type: 'DebugLoginUser', - email: userA.email, - password: userA.password, + type: 'DebugCreateUser', + ...mockUser, }), - }); - if (!response.ok) { - data = await fetch('http://localhost:3000/api/user/token', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - type: 'DebugCreateUser', - ...userA, - }), - }).then(r => r.json()); - setLoginStorage(data); - } else { - setLoginStorage((data = await response.json())); - } + }).then(r => r.json()); + setLoginStorage(data); loginResponseSchema.parse(data); }); @@ -69,6 +67,18 @@ declare global { } } +async function createWorkspace( + workspaceApi: typeof workspaceApis +): Promise { + const workspace = new Workspace({ + id: faker.datatype.uuid(), + }); + const binary = Workspace.Y.encodeStateAsUpdate(workspace.doc); + const data = await workspaceApi.createWorkspace(new Blob([binary])); + createWorkspaceResponseSchema.parse(data); + return data.id; +} + describe('api', () => { test('failed', async () => { workspaceApis = createWorkspaceApis('http://localhost:10086/404/'); @@ -100,12 +110,8 @@ describe('api', () => { test( 'create workspace', async () => { - const workspace = new Workspace({ - id: 'test', - }); - const binary = Workspace.Y.encodeStateAsUpdate(workspace.doc); - const data = await workspaceApis.createWorkspace(new Blob([binary])); - createWorkspaceResponseSchema.parse(data); + const id = await createWorkspace(workspaceApis); + expect(id).toBeTypeOf('string'); }, { timeout: 30000, @@ -115,13 +121,7 @@ describe('api', () => { test( 'delete workspace', async () => { - const workspace = new Workspace({ - id: 'test', - }); - const binary = Workspace.Y.encodeStateAsUpdate(workspace.doc); - const data = await workspaceApis.createWorkspace(new Blob([binary])); - createWorkspaceResponseSchema.parse(data); - const id = data.id; + const id = await createWorkspace(workspaceApis); const response = await workspaceApis.deleteWorkspace({ id, }); @@ -131,4 +131,58 @@ describe('api', () => { timeout: 30000, } ); + + test('get workspaces', async () => { + const id = await createWorkspace(workspaceApis); + const response = await workspaceApis.getWorkspaces(); + expect(response).toBeInstanceOf(Array); + expect(response.length).toBe(1); + expect(response[0].id).toBe(id); + }); + + test( + 'blob', + async () => { + const workspace = new Workspace({ + id: 'test', + }); + const path = require.resolve('@affine-test/fixtures/smile.png'); + const imageBuffer = await readFile(path); + const binary = Workspace.Y.encodeStateAsUpdate(workspace.doc); + const data = await workspaceApis.createWorkspace(new Blob([binary])); + createWorkspaceResponseSchema.parse(data); + const blobId = await workspaceApis.uploadBlob( + data.id, + imageBuffer, + 'image/png' + ); + expect(blobId).toBeTypeOf('string'); + const arrayBuffer = await workspaceApis.getBlob(blobId); + expect(arrayBuffer).toBeInstanceOf(ArrayBuffer); + expect(arrayBuffer.byteLength).toEqual(imageBuffer.byteLength); + expect(Buffer.from(arrayBuffer)).toEqual(imageBuffer); + }, + { + timeout: 30000, + } + ); + + test( + 'workspace binary', + async () => { + const id = await createWorkspace(workspaceApis); + await workspaceApis.updateWorkspace({ + id, + public: true, + }); + const binary = await workspaceApis.downloadWorkspace(id, false); + const publicBinary = await workspaceApis.downloadWorkspace(id, true); + expect(binary).toBeInstanceOf(ArrayBuffer); + expect(publicBinary).toBeInstanceOf(ArrayBuffer); + expect(binary.byteLength).toEqual(publicBinary.byteLength); + }, + { + timeout: 30000, + } + ); }); diff --git a/packages/workspace/src/affine/api/index.ts b/packages/workspace/src/affine/api/index.ts index 317a317445..fdc4bda754 100644 --- a/packages/workspace/src/affine/api/index.ts +++ b/packages/workspace/src/affine/api/index.ts @@ -295,22 +295,26 @@ export function createWorkspaceApis(prefixUrl = '/') { throw new RequestError(MessageCode.acceptInvitingFailed, e); }); }, - uploadBlob: async (workspaceId: string, blob: Blob): Promise => { + uploadBlob: async ( + workspaceId: string, + arrayBuffer: ArrayBuffer, + type: string + ): Promise => { const auth = getLoginStorage(); assertExists(auth); return fetch(prefixUrl + 'api/blob', { method: 'PUT', - body: blob, + body: arrayBuffer, headers: { - 'Content-Type': blob.type, + 'Content-Type': type, Authorization: auth.token, }, }).then(r => r.text()); }, - getBlob: async (params: { blobId: string }): Promise => { + getBlob: async (blobId: string): Promise => { const auth = getLoginStorage(); assertExists(auth); - return fetch(prefixUrl + `api/blob/${params.blobId}`, { + return fetch(prefixUrl + `api/blob/${blobId}`, { method: 'GET', headers: { Authorization: auth.token, diff --git a/yarn.lock b/yarn.lock index 75027195ca..a2376459f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2432,6 +2432,13 @@ __metadata: languageName: node linkType: hard +"@faker-js/faker@npm:^7.6.0": + version: 7.6.0 + resolution: "@faker-js/faker@npm:7.6.0" + checksum: 942af6221774e8c98a0eb6bc75265e05fb81a941170377666c3439aab9495dd321d6beedc5406f07e6ad44262b3e43c20961f666d116ad150b78e7437dd1bb2b + languageName: node + linkType: hard + "@fal-works/esbuild-plugin-global-externals@npm:^2.1.2": version: 2.1.2 resolution: "@fal-works/esbuild-plugin-global-externals@npm:2.1.2" @@ -7328,6 +7335,7 @@ __metadata: resolution: "AFFiNE@workspace:." dependencies: "@affine/cli": "workspace:*" + "@faker-js/faker": ^7.6.0 "@istanbuljs/schema": ^0.1.3 "@perfsee/sdk": ^1.5.1 "@playwright/test": ^1.32.1