diff --git a/.gitignore b/.gitignore index 1a51275..83bd64a 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,5 @@ dist .pnp.* lib +src/__tests__/mock/json/*.json +!src/__tests__/mock/json/mock*.json \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 75da8e1..2beaf36 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,11 +1,12 @@ -module.exports = { - testTimeout: 120000, - preset: 'ts-jest', - testEnvironment: 'node', - collectCoverageFrom: [ - "src/**/*.ts", // 包括 src 目录下所有的 TypeScript 文件 - "!src/**/*.d.ts", // 排除 TypeScript 声明文件 - "!src/__tests__/**/*.ts" - ], - testMatch: ["/src/__tests__/**/*.test.ts"] -}; +module.exports = { + testTimeout: 30000, + preset: 'ts-jest', + testEnvironment: 'node', + collectCoverageFrom: [ + "src/**/*.ts", // 包括 src 目录下所有的 TypeScript 文件 + "!src/**/*.d.ts", // 排除 TypeScript 声明文件 + "!src/__tests__/**/*.ts" + ], + testMatch: ["/src/__tests__/*.test.ts"], + testPathIgnorePatterns: [] +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a8c3463..7c9a6ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tiny-crud", - "version": "1.0.21", + "version": "1.0.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "tiny-crud", - "version": "1.0.21", + "version": "1.0.23", "license": "MIT", "devDependencies": { "@babel/preset-env": "^7.23.3", @@ -18,15 +18,19 @@ "@rollup/plugin-typescript": "^11.1.5", "@types/crypto-js": "^4.2.1", "@types/jest": "^29.5.9", + "@types/jsonfile": "^6.1.4", "@types/wechat-miniprogram": "^3.4.7", "axios": "^1.6.2", + "axios-mock-adapter": "^1.22.0", "crypto-js": "^4.2.0", "dayjs": "^1.11.10", "dotenv": "^16.3.1", "jest": "^29.7.0", + "jsonfile": "^6.1.0", "nyc": "^15.1.0", "rimraf": "^5.0.5", "rollup": "^4.3.0", + "rollup-plugin-dts": "^6.1.0", "ts-jest": "^29.1.1", "tslib": "^2.6.2", "typescript": "^5.2.2" @@ -2911,6 +2915,15 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", @@ -3062,6 +3075,19 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-mock-adapter": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz", + "integrity": "sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "is-buffer": "^2.0.5" + }, + "peerDependencies": { + "axios": ">= 0.17.0" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4038,6 +4064,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4402,6 +4434,29 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, "node_modules/is-builtin-module": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", @@ -6114,6 +6169,18 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -7196,6 +7263,28 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-dts": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.1.0.tgz", + "integrity": "sha512-ijSCPICkRMDKDLBK9torss07+8dl9UpY9z1N/zTeA1cIqdzMlpkV3MOOC7zukyvQfDyxa1s3Dl2+DeiP/G6DOw==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.4" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/Swatinem" + }, + "optionalDependencies": { + "@babel/code-frame": "^7.22.13" + }, + "peerDependencies": { + "rollup": "^3.29.4 || ^4", + "typescript": "^4.5 || ^5.0" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -7851,6 +7940,15 @@ "node": ">=4" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/package.json b/package.json index 1d8c24a..3f16564 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tiny-crud", - "version": "1.0.22", - "description": "", + "version": "1.0.23", + "description": "A tiny CRUD library based on Git Issue API", "main": "dist/bundle.cjs.js", "module": "dist/bundle.esm.js", "types": "dist/index.d.ts", @@ -13,7 +13,9 @@ "author": "Xicheng_Guo", "license": "MIT", "files": [ - "dist" + "dist/bundle.cjs.js", + "dist/bundle.esm.js", + "dist/index.d.ts" ], "devDependencies": { "@babel/preset-env": "^7.23.3", @@ -25,15 +27,19 @@ "@rollup/plugin-typescript": "^11.1.5", "@types/crypto-js": "^4.2.1", "@types/jest": "^29.5.9", + "@types/jsonfile": "^6.1.4", "@types/wechat-miniprogram": "^3.4.7", "axios": "^1.6.2", + "axios-mock-adapter": "^1.22.0", "crypto-js": "^4.2.0", "dayjs": "^1.11.10", "dotenv": "^16.3.1", "jest": "^29.7.0", + "jsonfile": "^6.1.0", "nyc": "^15.1.0", "rimraf": "^5.0.5", "rollup": "^4.3.0", + "rollup-plugin-dts": "^6.1.0", "ts-jest": "^29.1.1", "tslib": "^2.6.2", "typescript": "^5.2.2" diff --git a/rollup.config.mjs b/rollup.config.mjs index 5e39739..c9896ab 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -1,30 +1,39 @@ -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import babel from '@rollup/plugin-babel'; -import commonjs from '@rollup/plugin-commonjs'; -import json from '@rollup/plugin-json'; -import typescript from '@rollup/plugin-typescript'; +import { nodeResolve } from "@rollup/plugin-node-resolve"; +import babel from "@rollup/plugin-babel"; +import commonjs from "@rollup/plugin-commonjs"; +import json from "@rollup/plugin-json"; +import typescript from "@rollup/plugin-typescript"; +import dts from "rollup-plugin-dts"; -export default { - input: 'src/index.ts', - output: [ - { - file: 'dist/bundle.cjs.js', // CommonJS 输出文件 - format: 'cjs', - }, - { - file: 'dist/bundle.esm.js', // ES Module 输出文件 - format: 'esm', - }, - ], - plugins: [ - typescript({ include: ['./src/**/*.ts'] }), - babel({ - babelHelpers: 'bundled', - extensions: ['.ts', '.tsx'], - presets: ['@babel/preset-env', '@babel/preset-typescript'], - }), - nodeResolve(), - commonjs(), - json() - ] -}; +export default [ + { + input: "src/index.ts", + output: [ + { + file: "dist/bundle.cjs.js", // CommonJS 输出文件 + format: "cjs", + }, + { + file: "dist/bundle.esm.js", // ES Module 输出文件 + format: "esm", + }, + ], + plugins: [ + typescript({ include: ["./src/**/*.ts"] }), + babel({ + babelHelpers: "bundled", + extensions: [".ts", ".tsx"], + presets: ["@babel/preset-env", "@babel/preset-typescript"], + }), + nodeResolve(), + commonjs(), + json(), + ], + }, + /* 单独生成声明文件 */ + { + input: "dist/types/index.d.ts", + output: [{ file: "dist/index.d.ts", format: "es" }], + plugins: [dts()], + }, +]; diff --git a/src/__tests__/authenticate-gitee.test.ts b/src/__tests__/authenticate-gitee.test.ts index 3bd5f99..3f441e6 100644 --- a/src/__tests__/authenticate-gitee.test.ts +++ b/src/__tests__/authenticate-gitee.test.ts @@ -1,24 +1,30 @@ -import { giteeRequest } from "./helper/helper"; +import { USE_API, giteeRequest } from './helper/helper'; +import { mockGiteeUser } from './mock/mock-git-user'; describe('Test Authenticate Gitee', () => { - test('Test Authenticate Gitee', async () => { - const res = await giteeRequest.authenticate(); - expect(Object.keys(res)).toEqual([ - 'id', 'login', - 'name', 'avatar_url', - 'url', 'html_url', - 'remark', 'followers_url', - 'following_url', 'gists_url', - 'starred_url', 'subscriptions_url', - 'organizations_url', 'repos_url', - 'events_url', 'received_events_url', - 'type', 'blog', - 'weibo', 'bio', - 'public_repos', 'public_gists', - 'followers', 'following', - 'stared', 'watched', - 'created_at', 'updated_at', - 'email' - ]) - }); + beforeAll(()=>{ + if (USE_API) return; + mockGiteeUser(); + }); + + test('Test Authenticate Gitee', async () => { + const res = await giteeRequest.authenticate(); + expect(Object.keys(res)).toEqual([ + 'id', 'login', + 'name', 'avatar_url', + 'url', 'html_url', + 'remark', 'followers_url', + 'following_url', 'gists_url', + 'starred_url', 'subscriptions_url', + 'organizations_url', 'repos_url', + 'events_url', 'received_events_url', + 'type', 'blog', + 'weibo', 'bio', + 'public_repos', 'public_gists', + 'followers', 'following', + 'stared', 'watched', + 'created_at', 'updated_at', + 'email' + ]); + }); }); \ No newline at end of file diff --git a/src/__tests__/authenticate-github.test.ts b/src/__tests__/authenticate-github.test.ts index 0dbc16c..0534b0c 100644 --- a/src/__tests__/authenticate-github.test.ts +++ b/src/__tests__/authenticate-github.test.ts @@ -1,8 +1,16 @@ -import { githubRequest } from "./helper/helper"; +import { GithubUser } from "../index"; +import { USE_API, githubRequest } from "./helper/helper"; +import { mockGithubUser } from "./mock/mock-git-user"; describe('Test Authenticate Github', () => { + beforeAll(() => { + if (USE_API) return; + mockGithubUser(); + }); + test('Test Authenticate Github', async () => { - const res = await githubRequest.authenticate(); + const res = await githubRequest.authenticate() as GithubUser; + console.log(res.blog); expect(Object.keys(res)).toEqual([ 'login', 'id', 'node_id', 'avatar_url', diff --git a/src/__tests__/authenticate-gitlab.test.ts b/src/__tests__/authenticate-gitlab.test.ts index a5f7299..52df4b6 100644 --- a/src/__tests__/authenticate-gitlab.test.ts +++ b/src/__tests__/authenticate-gitlab.test.ts @@ -1,6 +1,12 @@ -import { gitlabRequest } from "./helper/helper"; +import { USE_API, gitlabRequest } from "./helper/helper"; +import { mockGitlabUser } from "./mock/mock-git-user"; describe('Test Authenticate Gitlab', () => { + beforeAll(() => { + if (USE_API) return; + mockGitlabUser(); + }); + test('Test Authenticate Gitlab', async () => { const res = await gitlabRequest.authenticate(); expect(Object.keys(res)).toEqual([ diff --git a/src/__tests__/book-repository.test.ts b/src/__tests__/book-repository.test.ts index 92fe450..b4f4885 100644 --- a/src/__tests__/book-repository.test.ts +++ b/src/__tests__/book-repository.test.ts @@ -1,7 +1,10 @@ import dayjs from "dayjs"; +import { PlainObject } from "../index"; import { BookModel } from "./helper/book-model"; +import { USE_API } from "./helper/helper"; import { Book } from "./helper/book-repository"; -import { PlainObject } from "../repository-lib"; +import { setupGithubMock } from "./mock/mock-github-api"; + describe('Test Book Storage', () => { @@ -59,7 +62,11 @@ describe('Test Book Storage', () => { ]; beforeAll(async () => { - await Book.deleteAll(); + if (USE_API) { + await Book.deleteAll(); + } else { + await setupGithubMock(); + } }); test('Test find Book', async () => { @@ -142,7 +149,7 @@ describe('Test Book Storage', () => { } }); - test('Test createAll Book', async () => { + (USE_API ? test: test.skip)('Test createAll Book', async () => { await Book.deleteAll(); const result = await Book.createAll(bookList); expect(result.length).toEqual(10); diff --git a/src/__tests__/chat-repository.test.ts b/src/__tests__/chat-repository.test.ts index 490addb..8d51167 100644 --- a/src/__tests__/chat-repository.test.ts +++ b/src/__tests__/chat-repository.test.ts @@ -1,6 +1,9 @@ -import { PlainObject } from "../repository-lib"; +import { PlainObject } from "../index"; import { ChatModel } from "./helper/chat-model"; import { Chat } from "./helper/chat-repository"; +import { USE_API } from "./helper/helper"; +import { setupGitlabMock} from "./mock/mock-gitlab-api"; + describe('Use Gitlab Test Chat Storage', () => { @@ -109,7 +112,11 @@ describe('Use Gitlab Test Chat Storage', () => { beforeAll(async () => { - await Chat.deleteAll(); + if (USE_API) { + await Chat.deleteAll(); + } else { + setupGitlabMock(); + } }); test('Test find Chat', async () => { @@ -198,7 +205,7 @@ describe('Use Gitlab Test Chat Storage', () => { } }); - test('Test createAll Chat', async () => { + (USE_API ? test: test.skip)('Test createAll Chat', async () => { await Chat.deleteAll(); const result = await Chat.createAll(chatList); expect(result.length).toEqual(10); @@ -210,8 +217,8 @@ describe('Use Gitlab Test Chat Storage', () => { test('Test createAll Chat orderly', async () => { await Chat.deleteAll(); const result = await Chat.createAll(chatList, true); + expect(result.length).toEqual(10); - // 因为是顺序创建,所以批量新增的数据是有序的 expect((await Chat.find()).map(item => item.participants)).toEqual(chatList.map(item => item.participants).reverse()); }); diff --git a/src/__tests__/helper/book-model.ts b/src/__tests__/helper/book-model.ts index f25a9c6..f78d017 100644 --- a/src/__tests__/helper/book-model.ts +++ b/src/__tests__/helper/book-model.ts @@ -1,4 +1,4 @@ -import { BaseModel } from '../../repository-lib/'; +import { BaseModel} from '../../index' export interface BookModel extends BaseModel { book_name: string; book_author: string; diff --git a/src/__tests__/helper/book-repository.ts b/src/__tests__/helper/book-repository.ts index 67bd7fa..24283e3 100644 --- a/src/__tests__/helper/book-repository.ts +++ b/src/__tests__/helper/book-repository.ts @@ -1,5 +1,4 @@ -import { GithubRepository } from "../../repository-lib"; -import { SingletonFactory } from "../../utils"; +import { GithubRepository, SingletonFactory } from "../../index"; import { BookModel } from "./book-model"; import { githubRequest } from "./helper"; diff --git a/src/__tests__/helper/chat-model.ts b/src/__tests__/helper/chat-model.ts index 8552b1a..20b051d 100644 --- a/src/__tests__/helper/chat-model.ts +++ b/src/__tests__/helper/chat-model.ts @@ -1,4 +1,4 @@ -import { BaseModel } from "../../repository-lib"; +import { BaseModel } from "../../index"; export interface ChatModel extends BaseModel { participants: string[]; diff --git a/src/__tests__/helper/chat-repository.ts b/src/__tests__/helper/chat-repository.ts index 8b553d9..539dc1b 100644 --- a/src/__tests__/helper/chat-repository.ts +++ b/src/__tests__/helper/chat-repository.ts @@ -1,7 +1,6 @@ -import { GitlabRepository } from "../../repository-lib"; +import { GitlabRepository, SingletonFactory } from "../../index"; import { ChatModel } from "./chat-model"; import { GITLAB_NUMBER, gitlabRequest } from "./helper"; -import { SingletonFactory } from "../../utils"; class ChatRepository extends GitlabRepository { constructor() { diff --git a/src/__tests__/helper/helper.ts b/src/__tests__/helper/helper.ts index 5d1859c..0d6b434 100644 --- a/src/__tests__/helper/helper.ts +++ b/src/__tests__/helper/helper.ts @@ -1,55 +1,89 @@ import 'dotenv/config'; import axios from 'axios'; import CryptoJS from 'crypto-js'; -import { createRequest } from '../../request-lib'; +import { createRequest } from '../../index'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; +import MockAdapter from 'axios-mock-adapter'; +import jsonfile from 'jsonfile'; + +// export const mock = new MockAdapter(axios); dayjs.extend(utc); dayjs.extend(timezone); - -// 设置默认时区 dayjs.tz.setDefault('Asia/Shanghai'); -export const GITEE_NUMBER = process.env.TEST_GITEE_NUMBER as string; +export const GITEE_NUMBER = process.env.TEST_GITEE_NUMBER || '1'; + +export const GITHUB_NUMBER = process.env.TEST_GITHUB_NUMBER || '1'; + +export const GITLAB_NUMBER = process.env.TEST_GITLAB_NUMBER || '1'; + +export const ENCRYPT_KEY = process.env.TEST_ENCRYPT_KEY || 'test-encrypt-key'; -export const GITHUB_NUMBER = process.env.TEST_GITHUB_NUMBER as string; +export const GITEE_OWNER = process.env.TEST_GITEE_OWNER || 'test-owner'; -export const GITLAB_NUMBER = process.env.TEST_GITLAB_NUMBER as string; +export const GITEE_REPO = process.env.TEST_GITEE_REPO || 'test-repo'; -export const TEST_ENCRYPT_KEY = process.env.TEST_ENCRYPT_KEY as string; +export const USE_API = process.env.USE_API === 'true' || false; + +export const GITHUB_OWNER = process.env.TEST_GITHUB_OWNER || 'test-owner'; + +export const GITHUB_REPO = process.env.TEST_GITHUB_REPO || 'test-repo'; + +export const GITLAB_PROJECT_ID = process.env.TEST_GITLAB_PROJECT_ID || 'test-project-id'; + +export let mock: MockAdapter | null = null; +if (!USE_API) { + mock = new MockAdapter(axios); +} export const giteeRequest = createRequest({ httpLib: 'axios', httpClient: axios, + accessToken: process.env.TEST_GITEE_TOKEN as string, + platform: 'gitee', - owner: process.env.TEST_GITEE_OWNER as string, - repo: process.env.TEST_GITEE_REPO as string, + owner: GITEE_OWNER, + repo: GITEE_REPO, + useEncrypt: true, encryptFn: (data: string) => { - return CryptoJS.AES.encrypt(data, TEST_ENCRYPT_KEY).toString(); + return CryptoJS.AES.encrypt(data, ENCRYPT_KEY).toString(); }, decryptFn: (data: string) => { - return CryptoJS.AES.decrypt(data, TEST_ENCRYPT_KEY).toString(CryptoJS.enc.Utf8); + return CryptoJS.AES.decrypt(data, ENCRYPT_KEY).toString(CryptoJS.enc.Utf8); } }); export const githubRequest = createRequest({ httpLib: 'axios', httpClient: axios, + accessToken: process.env.TEST_GITHUB_TOKEN as string, + platform: 'github', - owner: process.env.TEST_GITHUB_OWNER as string, - repo: process.env.TEST_GITHUB_REPO as string, - issueNumber: process.env.TEST_GITHUB_NUMBER as string + owner: GITHUB_OWNER, + repo: GITHUB_REPO, + issueNumber: GITHUB_NUMBER }); export const gitlabRequest = createRequest({ httpLib: 'axios', httpClient: axios, + accessToken: process.env.TEST_GITLAB_TOKEN as string, + platform: 'gitlab', - projectId: process.env.TEST_GITLAB_PROJECT_ID as string -}); \ No newline at end of file + projectId: GITLAB_PROJECT_ID +}); + +export function readJSONSync(filename: string) { + return jsonfile.readFileSync(`src/__tests__/mock/json/${filename}`); +} + +export function writeJSONSync(filename: string, data: any) { + return jsonfile.writeFileSync(`src/__tests__/mock/json/${filename}`, data, { spaces: 2 }); +} \ No newline at end of file diff --git a/src/__tests__/helper/user-model.ts b/src/__tests__/helper/user-model.ts index 5e673c0..e869dd6 100644 --- a/src/__tests__/helper/user-model.ts +++ b/src/__tests__/helper/user-model.ts @@ -1,4 +1,4 @@ -import { BaseModel } from "../../repository-lib"; +import { BaseModel } from "../../index"; export interface UserModel extends BaseModel { name: string; age: number; diff --git a/src/__tests__/helper/user-repository.ts b/src/__tests__/helper/user-repository.ts index 1216c4e..989b305 100644 --- a/src/__tests__/helper/user-repository.ts +++ b/src/__tests__/helper/user-repository.ts @@ -1,6 +1,5 @@ -import { GiteeRepository } from '../../repository-lib'; -import { SingletonFactory } from '../../utils'; +import { GiteeRepository, SingletonFactory } from '../../index'; import { GITEE_NUMBER, giteeRequest } from './helper'; import { UserModel } from './user-model'; diff --git a/src/__tests__/mock/json/.gitkeep b/src/__tests__/mock/json/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/__tests__/mock/json/mock-gitee-detail.json b/src/__tests__/mock/json/mock-gitee-detail.json new file mode 100644 index 0000000..08f505b --- /dev/null +++ b/src/__tests__/mock/json/mock-gitee-detail.json @@ -0,0 +1,224 @@ +{ + "id": 100001, + "url": "https://gitee.com/api/v5/repos/***/***/issues/***", + "repository_url": "https://gitee.com/api/v5/repos/***/***", + "labels_url": "https://gitee.com/api/v5/repos/***/***/issues/***/labels", + "comments_url": "https://gitee.com/api/v5/repos/***/***/issues/***/comments", + "html_url": "https://gitee.com/***/***/issues/***", + "parent_url": null, + "number": "***", + "parent_id": 0, + "depth": 0, + "state": "open", + "title": "***", + "body": "test body", + "user": { + "id": 100001, + "login": "***", + "name": "***", + "avatar_url": "https://foruda.gitee.com/avatar/***466/***59_***_***75.png", + "url": "https://gitee.com/api/v5/users/***", + "html_url": "https://gitee.com/***", + "remark": "", + "followers_url": "https://gitee.com/api/v5/users/***/followers", + "following_url": "https://gitee.com/api/v5/users/***/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/***/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/***/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/***/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/***/orgs", + "repos_url": "https://gitee.com/api/v5/users/***/repos", + "events_url": "https://gitee.com/api/v5/users/***/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/***/received_events", + "type": "User" + }, + "labels": [], + "assignee": null, + "collaborators": [], + "repository": { + "id": 100001, + "full_name": "***/***", + "human_name": "***/***", + "url": "https://gitee.com/api/v5/repos/***/***", + "namespace": { + "id": 100001, + "type": "personal", + "name": "***", + "path": "***", + "html_url": "https://gitee.com/***" + }, + "path": "***", + "name": "***", + "owner": { + "id": 100001, + "login": "***", + "name": "***", + "avatar_url": "https://foruda.gitee.com/avatar/***466/***59_***_***75.png", + "url": "https://gitee.com/api/v5/users/***", + "html_url": "https://gitee.com/***", + "remark": "", + "followers_url": "https://gitee.com/api/v5/users/***/followers", + "following_url": "https://gitee.com/api/v5/users/***/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/***/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/***/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/***/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/***/orgs", + "repos_url": "https://gitee.com/api/v5/users/***/repos", + "events_url": "https://gitee.com/api/v5/users/***/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/***/received_events", + "type": "User" + }, + "assigner": { + "id": 100001, + "login": "***", + "name": "***", + "avatar_url": "https://foruda.gitee.com/avatar/***466/***59_***_***75.png", + "url": "https://gitee.com/api/v5/users/***", + "html_url": "https://gitee.com/***", + "remark": "", + "followers_url": "https://gitee.com/api/v5/users/***/followers", + "following_url": "https://gitee.com/api/v5/users/***/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/***/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/***/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/***/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/***/orgs", + "repos_url": "https://gitee.com/api/v5/users/***/repos", + "events_url": "https://gitee.com/api/v5/users/***/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/***/received_events", + "type": "User" + }, + "description": "", + "private": true, + "public": false, + "internal": false, + "fork": false, + "html_url": "https://gitee.com/***/***.git", + "ssh_url": "git@gitee.com:***/***.git", + "forks_url": "https://gitee.com/api/v5/repos/***/***/forks", + "keys_url": "https://gitee.com/api/v5/repos/***/***/keys{/key_id}", + "collaborators_url": "https://gitee.com/api/v5/repos/***/***/collaborators{/collaborator}", + "hooks_url": "https://gitee.com/api/v5/repos/***/***/hooks", + "branches_url": "https://gitee.com/api/v5/repos/***/***/branches{/branch}", + "tags_url": "https://gitee.com/api/v5/repos/***/***/tags", + "blobs_url": "https://gitee.com/api/v5/repos/***/***/blobs{/sha}", + "stargazers_url": "https://gitee.com/api/v5/repos/***/***/stargazers", + "contributors_url": "https://gitee.com/api/v5/repos/***/***/contributors", + "commits_url": "https://gitee.com/api/v5/repos/***/***/commits{/sha}", + "comments_url": "https://gitee.com/api/v5/repos/***/***/comments{/number}", + "issue_comment_url": "https://gitee.com/api/v5/repos/***/***/issues/comments{/number}", + "issues_url": "https://gitee.com/api/v5/repos/***/***/issues{/number}", + "pulls_url": "https://gitee.com/api/v5/repos/***/***/pulls{/number}", + "milestones_url": "https://gitee.com/api/v5/repos/***/***/milestones{/number}", + "notifications_url": "https://gitee.com/api/v5/repos/***/***/notifications{?since,all,participating}", + "labels_url": "https://gitee.com/api/v5/repos/***/***/labels{/name}", + "releases_url": "https://gitee.com/api/v5/repos/***/***/releases{/id}", + "recommend": false, + "gvp": false, + "homepage": null, + "language": null, + "forks_count": 0, + "stargazers_count": 0, + "watchers_count": 1, + "default_branch": null, + "open_issues_count": 1, + "has_issues": true, + "has_wiki": true, + "issue_comment": false, + "can_comment": true, + "pull_requests_enabled": true, + "has_page": false, + "license": null, + "outsourced": false, + "project_creator": "***", + "members": [ + "***" + ], + "pushed_at": null, + "created_at": "2023-11-16T22:03:27+08:00", + "updated_at": "2023-11-16T22:03:44+08:00", + "parent": null, + "paas": null, + "assignees_number": 1, + "testers_number": 1, + "assignee": [ + { + "id": 100001, + "login": "***", + "name": "***", + "avatar_url": "https://foruda.gitee.com/avatar/***466/***59_***_***75.png", + "url": "https://gitee.com/api/v5/users/***", + "html_url": "https://gitee.com/***", + "remark": "", + "followers_url": "https://gitee.com/api/v5/users/***/followers", + "following_url": "https://gitee.com/api/v5/users/***/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/***/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/***/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/***/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/***/orgs", + "repos_url": "https://gitee.com/api/v5/users/***/repos", + "events_url": "https://gitee.com/api/v5/users/***/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/***/received_events", + "type": "User" + } + ], + "testers": [ + { + "id": 100001, + "login": "***", + "name": "***", + "avatar_url": "https://foruda.gitee.com/avatar/***466/***59_***_***75.png", + "url": "https://gitee.com/api/v5/users/***", + "html_url": "https://gitee.com/***", + "remark": "", + "followers_url": "https://gitee.com/api/v5/users/***/followers", + "following_url": "https://gitee.com/api/v5/users/***/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/***/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/***/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/***/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/***/orgs", + "repos_url": "https://gitee.com/api/v5/users/***/repos", + "events_url": "https://gitee.com/api/v5/users/***/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/***/received_events", + "type": "User" + } + ], + "status": "开始", + "programs": [], + "enterprise": null, + "project_labels": [], + "issue_template_source": "project" + }, + "milestone": null, + "created_at": "2023-11-16T22:03:44+08:00", + "updated_at": "2024-01-12T11:31:23+08:00", + "plan_started_at": null, + "deadline": null, + "finished_at": null, + "scheduled_time": 0, + "comments": 720, + "priority": 0, + "issue_type": "任务", + "program": null, + "security_hole": false, + "issue_state": "待办的", + "branch": null, + "issue_type_detail": { + "id": 1, + "title": "任务", + "template": null, + "ident": "task", + "color": "#0086D6", + "is_system": true, + "created_at": "2017-09-01T03:09:12+08:00", + "updated_at": "2017-09-01T03:09:12+08:00" + }, + "issue_state_detail": { + "id": 1, + "title": "待办的", + "color": "#8c92a4", + "icon": "icon-task-state-21", + "command": null, + "serial": 1, + "created_at": "2017-09-01T03:09:13+08:00", + "updated_at": "2017-09-01T03:09:13+08:00" + } +} \ No newline at end of file diff --git a/src/__tests__/mock/json/mock-gitee-user.json b/src/__tests__/mock/json/mock-gitee-user.json new file mode 100644 index 0000000..8f0d7fe --- /dev/null +++ b/src/__tests__/mock/json/mock-gitee-user.json @@ -0,0 +1,31 @@ +{ + "id": 1000001, + "login": "***", + "name": "***", + "avatar_url": "https://foruda.gitee.com/avatar/***/***.png", + "url": "https://gitee.com/api/v5/users/***", + "html_url": "https://gitee.com/***", + "remark": "", + "followers_url": "https://gitee.com/api/v5/users/***/followers", + "following_url": "https://gitee.com/api/v5/users/***/following_url{/other_user}", + "gists_url": "https://gitee.com/api/v5/users/***/gists{/gist_id}", + "starred_url": "https://gitee.com/api/v5/users/***/starred{/owner}{/repo}", + "subscriptions_url": "https://gitee.com/api/v5/users/***/subscriptions", + "organizations_url": "https://gitee.com/api/v5/users/***/orgs", + "repos_url": "https://gitee.com/api/v5/users/***/repos", + "events_url": "https://gitee.com/api/v5/users/***/events{/privacy}", + "received_events_url": "https://gitee.com/api/v5/users/***/received_events", + "type": "User", + "blog": null, + "weibo": null, + "bio": "", + "public_repos": 0, + "public_gists": 0, + "followers": 0, + "following": 0, + "stared": 0, + "watched": 0, + "created_at": "2020-02-26T13:20:27+08:00", + "updated_at": "2024-01-10T22:27:59+08:00", + "email": null +} \ No newline at end of file diff --git a/src/__tests__/mock/json/mock-github-detail.json b/src/__tests__/mock/json/mock-github-detail.json new file mode 100644 index 0000000..f7d0b81 --- /dev/null +++ b/src/__tests__/mock/json/mock-github-detail.json @@ -0,0 +1,61 @@ +{ + "url": "https://api.github.com/repos/***/***/issues/***", + "repository_url": "https://api.github.com/repos/***/***", + "labels_url": "https://api.github.com/repos/***/***/issues/***/labels{/name}", + "comments_url": "https://api.github.com/repos/***/***/issues/***/comments", + "events_url": "https://api.github.com/repos/***/***/issues/***/events", + "html_url": "https://github.com/***/***/issues/***", + "id": 1000001, + "node_id": "I_k***IxW", + "number": 0, + "title": "For ***** Test", + "user": { + "login": "***", + "id": 1000001, + "node_id": "MDQ*****DA5", + "avatar_url": "https://avatars.git*****tent.com/u/3*****9?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/***", + "html_url": "https://github.com/***", + "followers_url": "https://api.github.com/users/***/followers", + "following_url": "https://api.github.com/users/***/following{/other_user}", + "gists_url": "https://api.github.com/users/***/gists{/gist_id}", + "starred_url": "https://api.github.com/users/***/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/***/subscriptions", + "organizations_url": "https://api.github.com/users/***/orgs", + "repos_url": "https://api.github.com/users/***/repos", + "events_url": "https://api.github.com/users/***/events{/privacy}", + "received_events_url": "https://api.github.com/users/***/received_events", + "type": "User", + "site_admin": false + }, + "labels": [], + "state": "open", + "locked": false, + "assignee": null, + "assignees": [], + "milestone": null, + "comments": 0, + "created_at": "2023-11-21T07:10:21Z", + "updated_at": "2024-01-11T01:58:16Z", + "closed_at": null, + "author_association": "OWNER", + "active_lock_reason": null, + "body": "test for body", + "closed_by": null, + "reactions": { + "url": "https://api.github.com/repos/***/***/issues/***/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "timeline_url": "https://api.github.com/repos/***/***/issues/***/timeline", + "performed_via_github_app": null, + "state_reason": null +} \ No newline at end of file diff --git a/src/__tests__/mock/json/mock-github-user.json b/src/__tests__/mock/json/mock-github-user.json new file mode 100644 index 0000000..6568d3f --- /dev/null +++ b/src/__tests__/mock/json/mock-github-user.json @@ -0,0 +1,34 @@ +{ + "login": "***", + "id": 1000001, + "node_id": "***", + "avatar_url": "https://avatars.githubusercontent.com/u/***?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/***", + "html_url": "https://github.com/***", + "followers_url": "https://api.github.com/users/***/followers", + "following_url": "https://api.github.com/users/***/following{/other_user}", + "gists_url": "https://api.github.com/users/***/gists{/gist_id}", + "starred_url": "https://api.github.com/users/***/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/***/subscriptions", + "organizations_url": "https://api.github.com/users/***/orgs", + "repos_url": "https://api.github.com/users/***/repos", + "events_url": "https://api.github.com/users/***/events{/privacy}", + "received_events_url": "https://api.github.com/users/***/received_events", + "type": "User", + "site_admin": false, + "name": "***", + "company": null, + "blog": "***.github.io", + "location": "***", + "email": null, + "hireable": null, + "bio": "***", + "twitter_username": null, + "public_repos": 0, + "public_gists": 0, + "followers": 0, + "following": 0, + "created_at": "2017-12-04T12:29:35Z", + "updated_at": "2023-12-17T04:01:22Z" +} \ No newline at end of file diff --git a/src/__tests__/mock/json/mock-gitlab-detail.json b/src/__tests__/mock/json/mock-gitlab-detail.json new file mode 100644 index 0000000..071541f --- /dev/null +++ b/src/__tests__/mock/json/mock-gitlab-detail.json @@ -0,0 +1,64 @@ +{ + "id": 100001, + "iid": 2, + "project_id": 100001, + "title": "For *** Test New", + "description": "test", + "state": "opened", + "created_at": "2024-01-02T02:27:57.658Z", + "updated_at": "2024-01-15T08:06:48.811Z", + "closed_at": null, + "closed_by": null, + "labels": [], + "milestone": null, + "assignees": [], + "author": { + "id": 100001, + "username": "***", + "name": "***", + "state": "active", + "locked": false, + "avatar_url": "https://gitlab.com/uploads/-/system/user/avatar/***/avatar.png", + "web_url": "https://gitlab.com/***" + }, + "type": "ISSUE", + "assignee": null, + "user_notes_count": 1, + "merge_requests_count": 0, + "upvotes": 0, + "downvotes": 0, + "due_date": null, + "confidential": false, + "discussion_locked": null, + "issue_type": "issue", + "web_url": "https://gitlab.com/***/***/-/issues/2", + "time_stats": { + "time_estimate": 0, + "total_time_spent": 0, + "human_time_estimate": null, + "human_total_time_spent": null + }, + "task_completion_status": { + "count": 0, + "completed_count": 0 + }, + "blocking_issues_count": 0, + "has_tasks": true, + "task_status": "0 of 0 checklist items completed", + "_links": { + "self": "https://gitlab.com/api/v4/projects/***/issues/2", + "notes": "https://gitlab.com/api/v4/projects/***/issues/2/notes", + "award_emoji": "https://gitlab.com/api/v4/projects/***/issues/2/award_emoji", + "project": "https://gitlab.com/api/v4/projects/***", + "closed_as_duplicate_of": null + }, + "references": { + "short": "#2", + "relative": "#2", + "full": "***/***#2" + }, + "severity": "UNKNOWN", + "subscribed": true, + "moved_to_id": null, + "service_desk_reply_to": null +} \ No newline at end of file diff --git a/src/__tests__/mock/json/mock-gitlab-user.json b/src/__tests__/mock/json/mock-gitlab-user.json new file mode 100644 index 0000000..9cf7e87 --- /dev/null +++ b/src/__tests__/mock/json/mock-gitlab-user.json @@ -0,0 +1,42 @@ +{ + "id": 100001, + "username": "***", + "name": "***", + "state": "active", + "locked": false, + "avatar_url": "https://secure.gravatar.com/avatar/***?s=80&d=identicon", + "web_url": "https://gitlab.com/***", + "created_at": "2023-12-21T02:56:43.269Z", + "bio": "", + "location": "", + "public_email": null, + "skype": "", + "linkedin": "", + "twitter": "", + "discord": "", + "website_url": "", + "organization": "", + "job_title": "", + "pronouns": null, + "bot": true, + "work_information": null, + "local_time": null, + "last_sign_in_at": null, + "confirmed_at": "2023-12-21T02:56:43.230Z", + "last_activity_on": "2024-01-12", + "email": "***@noreply.gitlab.com", + "theme_id": 0, + "color_scheme_id": 0, + "projects_limit": 0, + "current_sign_in_at": null, + "identities": [], + "can_create_group": true, + "can_create_project": true, + "two_factor_enabled": false, + "external": false, + "private_profile": false, + "commit_email": "***@noreply.gitlab.com", + "shared_runners_minutes_limit": null, + "extra_shared_runners_minutes_limit": null, + "scim_identities": [] +} \ No newline at end of file diff --git a/src/__tests__/mock/mock-git-user.ts b/src/__tests__/mock/mock-git-user.ts new file mode 100644 index 0000000..9801013 --- /dev/null +++ b/src/__tests__/mock/mock-git-user.ts @@ -0,0 +1,19 @@ +import { mock, readJSONSync } from "../helper/helper"; + +export function mockGiteeUser() { + mock?.onGet('https://gitee.com/api/v5/user').reply(async () => { + return [200, readJSONSync('mock-gitee-user.json')]; + }); +} + +export function mockGithubUser() { + mock?.onGet('https://api.github.com/user').reply(async () => { + return [200, readJSONSync('mock-github-user.json')]; + }); +} + +export function mockGitlabUser() { + mock?.onGet('https://gitlab.com/api/v4/user').reply(async () => { + return [200, readJSONSync('mock-gitlab-user.json')] + }); +} \ No newline at end of file diff --git a/src/__tests__/mock/mock-gitee-api.ts b/src/__tests__/mock/mock-gitee-api.ts new file mode 100644 index 0000000..1b8d215 --- /dev/null +++ b/src/__tests__/mock/mock-gitee-api.ts @@ -0,0 +1,105 @@ +import dayjs from "dayjs"; +import { GITEE_NUMBER, GITEE_OWNER, GITEE_REPO, mock, readJSONSync, writeJSONSync } from "../helper/helper"; + +const filename = "temp-gitee.json"; + +export function setupGiteeMock() { + writeJSONSync(filename, []); + mockGiteeFind(); + mockGiteeCreate(); + mockGiteeFindById(); + mockGiteeUpdateById(); + mockGiteeDeleteById(); + mockGiteeDetail(); +} + +function mockGiteeFind() { + mock?.onGet(`https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/issues/${GITEE_NUMBER}/comments`).reply(async (config) => { + const result = readJSONSync(filename); + if (config.params?.since) { + return [200, result.filter((item: any) => dayjs(item.created_at).isAfter(dayjs(config.params.since)))]; + } + if (config.params?.order) { + return [200, config.params.order == 'desc' ? result.reverse() : result]; + } + if (config.params?.page && config.params?.per_page) { + const start = (config.params.page - 1) * config.params.per_page; + const end = config.params.page * config.params.per_page; + return [200, result.slice(start, end)]; + } + return [200, result]; + }); +} + +function mockGiteeFindById() { + mock?.onGet(new RegExp(`https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/issues/comments/\\d+`)).reply(async (config) => { + const result = readJSONSync(filename); + const id = config.url?.match(/\/issues\/comments\/(\d+)/)?.[1]; + const target = result.find((item: any) => item.id == id); + if (!target) { + return [404, { message: '404 Not Found' }]; + } + return [200, target]; + }); +} + +function mockGiteeCreate() { + mock?.onPost(`https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/issues/${GITEE_NUMBER}/comments`).reply(async (config) => { + const result = readJSONSync(filename); + const data = { + id: Math.round(Math.random() * 1000000), + body: JSON.parse(config.data).body, + user: { + id: 100001, + login: "***", + name: "***", + avatar_url: "https://foruda.gitee.com/avatar/***/***.png", + }, + created_at: dayjs().format(), + updated_at: dayjs().format() + }; + result.push(data); + writeJSONSync(filename, result); + return [200, data]; + }); +} + +function mockGiteeUpdateById() { + mock?.onPatch(new RegExp(`https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/issues/comments/\\d+`)).reply(async (config) => { + const raw = readJSONSync(filename); + const id = config.url?.match(/\/issues\/comments\/(\d+)/)?.[1]; + const target = raw.find((item: any) => item.id == id); + if (!target) { + return [404, { message: '404 Not Found' }]; + } + raw.forEach((item: any) => { + if (item.id == id) { + item.body = JSON.parse(config.data).body; + item.updated_at = dayjs().format(); + } + }); + writeJSONSync(filename, raw); + const resAfter = readJSONSync(filename); + return [200, resAfter.find((item: any) => item.id == id)]; + }); +} + +function mockGiteeDeleteById() { + mock?.onDelete(new RegExp(`https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/issues/comments/\\d+`)).reply(async (config) => { + const raw = readJSONSync(filename); + const id = config.url?.match(/\/issues\/comments\/(\d+)/)?.[1]; + const target = raw.find((item: any) => item.id == id); + if (!target) { + return [404, { message: '404 Not Found' }]; + } + const remain = raw.find((item: any) => item.id != id); + writeJSONSync(filename, remain ? remain : []); + return [204]; + }); +} + +function mockGiteeDetail() { + mock?.onGet(new RegExp(`https://gitee.com/api/v5/repos/${GITEE_OWNER}/${GITEE_REPO}/issues/${GITEE_NUMBER}`)).reply(async (config) => { + return [200, readJSONSync('mock-gitee-detail.json')]; + }); +} \ No newline at end of file diff --git a/src/__tests__/mock/mock-github-api.ts b/src/__tests__/mock/mock-github-api.ts new file mode 100644 index 0000000..63feade --- /dev/null +++ b/src/__tests__/mock/mock-github-api.ts @@ -0,0 +1,111 @@ +import dayjs from "dayjs"; +import { GITHUB_NUMBER, GITHUB_OWNER, GITHUB_REPO, mock, readJSONSync, writeJSONSync } from "../helper/helper"; + +const filename = "temp-github.json"; + +export function setupGithubMock() { + writeJSONSync(filename, []); + mockGithubFind(); + mockGithubCreate(); + mockGithubFindById(); + mockGiteeUpdateById(); + mockGiteeDeleteById(); + mockGiteeDetail(); +}; + +function mockGithubFind() { + mock?.onGet(`https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/issues/${GITHUB_NUMBER}/comments`).reply(async (config) => { + const result = readJSONSync(filename); + if (config.params?.since) { + return [200, result.filter((item: any) => dayjs(item.created_at).isAfter(dayjs(config.params.since)))]; + } + if (config.params?.page && config.params?.per_page) { + const start = (config.params.page - 1) * config.params.per_page; + const end = config.params.page * config.params.per_page; + return [200, result.slice(start, end)]; + } + return [200, result]; + }); +} + +function mockGithubFindById() { + mock?.onGet(new RegExp(`https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/issues/comments/\\d+`)).reply(async (config) => { + const result = readJSONSync(filename); + const id = config.url?.match(/\/issues\/comments\/(\d+)/)?.[1]; + const target = result.find((item: any) => item.id == id); + if (!target) { + return [404, { + "message": "Not Found", + "documentation_url": "https://docs.github.com/rest/issues/comments#get-an-issue-comment" + }]; + } + return [200, target]; + }); +} + +function mockGithubCreate() { + mock?.onPost(`https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/issues/${GITHUB_NUMBER}/comments`).reply(async (config) => { + const result = readJSONSync(filename); + const data = { + id: Math.round(Math.random() * 1000000), + body: JSON.parse(config.data).body, + user: { + id: 100001, + login: "***", + name: "***", + avatar_url: "https://foruda.github.com/avatar/***/***.png", + }, + created_at: dayjs().format(), + updated_at: dayjs().format() + }; + result.push(data); + writeJSONSync(filename, result); + return [200, data]; + }); +} + +function mockGiteeUpdateById() { + mock?.onPatch(new RegExp(`https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/issues/comments/\\d+`)).reply(async (config) => { + const raw = readJSONSync(filename); + const id = config.url?.match(/\/issues\/comments\/(\d+)/)?.[1]; + const target = raw.find((item: any) => item.id == id); + if (!target) { + return [404, { + "message": "Not Found", + "documentation_url": "https://docs.github.com/rest/issues/comments#update-an-issue-comment" + }]; + } + raw.forEach((item: any) => { + if (item.id == id) { + item.body = JSON.parse(config.data).body; + item.updated_at = dayjs().format(); + } + }); + writeJSONSync(filename, raw); + const resAfter = readJSONSync(filename); + return [200, resAfter.find((item: any) => item.id == id)]; + }); +} + +function mockGiteeDeleteById() { + mock?.onDelete(new RegExp(`https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/issues/comments/\\d+`)).reply(async (config) => { + const raw = readJSONSync(filename); + const id = config.url?.match(/\/issues\/comments\/(\d+)/)?.[1]; + const target = raw.find((item: any) => item.id == id); + if (!target) { + return [404, { + "message": "Not Found", + "documentation_url": "https://docs.github.com/rest/issues/comments#delete-an-issue-comment" + }]; + } + const remain = raw.find((item: any) => item.id != id); + writeJSONSync(filename, remain ? remain : []); + return [204]; + }); +} + +function mockGiteeDetail() { + mock?.onGet(new RegExp(`https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/issues/${GITHUB_NUMBER}`)).reply(async (config) => { + return [200, readJSONSync('mock-github-detail.json')]; + }); +} \ No newline at end of file diff --git a/src/__tests__/mock/mock-gitlab-api.ts b/src/__tests__/mock/mock-gitlab-api.ts new file mode 100644 index 0000000..a56d557 --- /dev/null +++ b/src/__tests__/mock/mock-gitlab-api.ts @@ -0,0 +1,112 @@ +import dayjs from "dayjs"; +import { GITLAB_NUMBER, GITLAB_PROJECT_ID, mock, readJSONSync, writeJSONSync } from "../helper/helper"; + +const filename = "temp-gitlab.json"; + +export function setupGitlabMock() { + writeJSONSync(filename, []); + mockGitlabFind(); + mockGitlabCreate(); + mockGitlabFindById(); + mockGitlabUpdateById(); + mockGitlabDeleteById(); + mockGitlabDetail(); +} + +function mockGitlabFind() { + mock?.onGet(`https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/issues/${GITLAB_NUMBER}/notes`).reply(async (config) => { + const result = readJSONSync(filename); + if (config.params?.sort) { + const sort = config.params?.sort; + result.sort((a: any, b: any) => { + if (sort == "asc") { + return dayjs(a.created_at).isBefore(dayjs(b.created_at)) ? -1 : 1; + } else { + return dayjs(a.created_at).isAfter(dayjs(b.created_at)) ? -1 : 1; + } + }); + } + return [200, result]; + }); +} + +function mockGitlabFindById() { + mock?.onGet(new RegExp(`https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/issues/${GITLAB_NUMBER}/notes/\\d+`)).reply(async (config) => { + const result = readJSONSync(filename); + const id = config.url?.match(/\/notes\/(\d+)/)?.[1]; + const target = result.find((item: any) => item.id == id); + if (!target) { + return [404, { + "message": "404 Not found" + }]; + } + return [200, target]; + }); +} + +function mockGitlabCreate() { + mock?.onPost(`https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/issues/${GITLAB_NUMBER}/notes`).reply(async (config) => { + const result = readJSONSync(filename); + const data = { + id: Math.round(Math.random() * 1000000), + body: JSON.parse(config.data).body, + system: false, + author: { + id: 100001, + username: "***", + name: "***", + avatar_url: "https://foruda.gitlab.com/avatar/***/***.png", + }, + created_at: dayjs().format(), + updated_at: dayjs().format() + }; + result.unshift(data); + await new Promise(resolve => setTimeout(resolve, 1000)); + writeJSONSync(filename, result); + return [200, data]; + }); +} + +function mockGitlabUpdateById() { + mock?.onPut(new RegExp(`https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/issues/${GITLAB_NUMBER}/notes/\\d+`)).reply(async (config) => { + const raw = readJSONSync(filename); + const id = config.url?.match(/\/notes\/(\d+)/)?.[1]; + const target = raw.find((item: any) => item.id == id); + if (!target) { + return [404, { + "message": "404 Not found" + }]; + } + raw.forEach((item: any) => { + if (item.id == id) { + item.body = JSON.parse(config.data).body; + item.updated_at = dayjs().format(); + } + }); + writeJSONSync(filename, raw); + const resAfter = readJSONSync(filename); + return [200, resAfter.find((item: any) => item.id == id)]; + }); +} + +function mockGitlabDeleteById() { + mock?.onDelete(new RegExp(`https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/issues/${GITLAB_NUMBER}/notes/\\d+`)).reply(async (config) => { + const raw = readJSONSync(filename); + const id = config.url?.match(/\/notes\/(\d+)/)?.[1]; + const target = raw.find((item: any) => item.id == id); + if (!target) { + return [404, { + "message": "404 Not found" + }]; + } + const remain = raw.find((item: any) => item.id != id); + writeJSONSync(filename, remain ? remain : []); + return [204]; + }); +} + +function mockGitlabDetail() { + mock?.onGet(new RegExp(`https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/issues/${GITLAB_NUMBER}`)).reply(async (config) => { + return [200, readJSONSync('mock-gitlab-detail.json')]; + }); +} \ No newline at end of file diff --git a/src/__tests__/user-repository.test.ts b/src/__tests__/user-repository.test.ts index 8e8deb4..b348894 100644 --- a/src/__tests__/user-repository.test.ts +++ b/src/__tests__/user-repository.test.ts @@ -1,10 +1,12 @@ -import { PlainObject } from "../repository-lib"; +import dayjs from "dayjs"; +import { PlainObject } from "../index"; import { UserModel } from "./helper/user-model"; import { User } from "./helper/user-repository"; -import dayjs from 'dayjs'; +import { setupGiteeMock } from "./mock/mock-gitee-api"; +import { USE_API } from "./helper/helper"; -describe('Test User Storage', () => { +describe('Test User Repository', () => { const userList: PlainObject[] = [{ name: 'test-user-1', age: 18 @@ -45,9 +47,13 @@ describe('Test User Storage', () => { name: 'test-user-10', age: 36 }]; - - beforeAll(async () => { - await User.deleteAll(); + + beforeAll(async ()=>{ + if (USE_API) { + await User.deleteAll(); + } else { + setupGiteeMock(); + } }); test('Test find User', async () => { @@ -60,6 +66,7 @@ describe('Test User Storage', () => { name: 'test-user', age: 18 }); + const findResult = await User.find(); expect(findResult.length).toEqual(1); @@ -77,6 +84,7 @@ describe('Test User Storage', () => { name: 'test-user-update', age: 20 }); + const findByIdResult = await User.findById(findResult[0].id); expect(updateResult.name).toEqual(findByIdResult.name); expect(updateResult.age).toEqual(findByIdResult.age); @@ -112,7 +120,7 @@ describe('Test User Storage', () => { } }); - test('Test createAll User', async () => { + (USE_API ? test: test.skip)('Test createAll User', async () => { await User.deleteAll(); const result = await User.createAll(userList); expect(result.length).toEqual(10); diff --git a/src/__tests__/wx-request.test.ts b/src/__tests__/wx-request.test.ts new file mode 100644 index 0000000..203f243 --- /dev/null +++ b/src/__tests__/wx-request.test.ts @@ -0,0 +1,88 @@ +import { createRequest } from '../index'; + +const wx = { + request: jest.fn() +}; + +const request = createRequest({ + httpLib: 'wx', + httpClient: wx as any as WechatMiniprogram.Wx, + platform: 'gitee', + owner: 'test-owner', + repo: 'test-repo', + accessToken: 'test-token' +}); + +describe('Test Wx Request', () => { + test('wx get success', async () => { + wx.request.mockImplementation(({ success }) => { + success({ data: 'success' }); + }); + const result = await request.get('test-url', { order: 'desc' }); + expect(result).toEqual('success'); + expect(wx.request).toHaveBeenCalledWith({ + 'fail': expect.any(Function), + 'data': { + 'order': 'desc' + }, + 'header': { + 'Authorization': 'Bearer test-token' + }, + 'method': 'GET', + 'success': expect.any(Function), + 'url': 'test-url' + }); + }); + + test('wx get fail', async () => { + wx.request.mockImplementation(({ fail }) => { + fail('fail'); + }); + try { + await request.get('test-url'); + } catch (error) { + expect(error).toEqual('fail'); + } + }); + + test('wx post success', async () => { + wx.request.mockImplementation(({ success }) => { + success({ data: 'success' }); + }); + const result = await request.post('test-url', 'test-body'); + expect(result).toEqual('success'); + expect(wx.request).toHaveBeenCalledWith({ + 'fail': expect.any(Function), + 'data': { + 'body': 'test-body' + }, + 'header': { + 'Authorization': 'Bearer test-token' + }, + 'method': 'POST', + 'success': expect.any(Function), + 'url': 'test-url' + }); + }); + + test('wx patch success', async () => { + wx.request.mockImplementation(({ success }) => { + success({ data: 'success' }); + }); + const result = await request.patch('test-url', 'test-body'); + expect(result).toEqual('success'); + expect(wx.request).toHaveBeenCalledWith({ + 'fail': expect.any(Function), + 'data': { + 'body': 'test-body' + }, + 'header': { + 'Authorization': 'Bearer test-token', + 'X-HTTP-Method-Override': 'PATCH' + }, + 'method': 'POST', + 'success': expect.any(Function), + 'url': 'test-url' + }); + }); +}) \ No newline at end of file diff --git a/src/__tests__/wx-request.ts b/src/__tests__/wx-request.ts deleted file mode 100644 index ce88fbb..0000000 --- a/src/__tests__/wx-request.ts +++ /dev/null @@ -1,88 +0,0 @@ -// import { createRequest } from '../request-lib'; - -// const wx = { -// request: jest.fn() -// }; - -// const request = createRequest({ -// httpLib: 'wx', -// httpClient: wx, -// platform: 'gitee', -// owner: 'test-owner', -// repo: 'test-repo', -// accessToken: 'test-token' -// }); - -// describe('Test Wx Request', () => { -// test('wx get success', async () => { -// wx.request.mockImplementation(({ success }) => { -// success({data: 'success'}); -// }); -// const result = await request.get('test-url', { order: 'desc'}); -// expect(result).toEqual('success'); -// expect(wx.request).toHaveBeenCalledWith({ -// 'fail': expect.any(Function), -// 'data': { -// 'order': 'desc' -// }, -// 'header': { -// 'Authorization': 'Bearer test-token' -// }, -// 'method': 'GET', -// 'success': expect.any(Function), -// 'url': 'test-url' -// }); -// }); - -// test('wx get fail', async () => { -// wx.request.mockImplementation(({ fail }) => { -// fail('fail'); -// }); -// try { -// await request.get('test-url'); -// } catch (error) { -// expect(error).toEqual('fail'); -// } -// }); - -// test('wx post success', async () => { -// wx.request.mockImplementation(({ success }) => { -// success({data: 'success'}); -// }); -// const result = await request.post('test-url', 'test-body'); -// expect(result).toEqual('success'); -// expect(wx.request).toHaveBeenCalledWith({ -// 'fail': expect.any(Function), -// 'data': { -// 'body': 'test-body' -// }, -// 'header': { -// 'Authorization': 'Bearer test-token' -// }, -// 'method': 'POST', -// 'success': expect.any(Function), -// 'url': 'test-url' -// }); -// }); - -// test('wx patch success', async () => { -// wx.request.mockImplementation(({ success }) => { -// success({data: 'success'}); -// }); -// const result = await request.patch('test-url', 'test-body'); -// expect(result).toEqual('success'); -// expect(wx.request).toHaveBeenCalledWith({ -// 'fail': expect.any(Function), -// 'data': { -// 'body': 'test-body' -// }, -// 'header': { -// 'Authorization': 'Bearer test-token', -// 'X-HTTP-Method-Override': 'PATCH' -// }, -// 'method': 'POST', -// 'success': expect.any(Function), -// 'url': 'test-url' -// }); -// }); -// }); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 41c8aa2..ef2a4f7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,22 @@ -import { createRequest } from "./request-lib/create-request"; -import { BaseModel, GiteeRepository, GitlabRepository, GithubRepository, PlainObject } from "./repository-lib"; -import { SingletonFactory } from "./utils" -export { - createRequest, BaseModel, PlainObject, - GiteeRepository, GitlabRepository, GithubRepository, SingletonFactory -}; +export { createRequest } from './request-lib/create-request'; + +export { SingletonFactory } from './utils/singleton-factory'; + +export { BaseRepository } from './repository-lib/base/base-repository'; + +export { BaseModel } from './repository-lib/base/base-model'; + +export { PlainObject } from './repository-lib/base/plain-object'; + +export { GiteeRepository } from './repository-lib/gitee/gitee-repository'; + +export { GithubRepository } from './repository-lib/github/github-repository'; + +export { GitlabRepository } from './repository-lib/gitlab/gitlab-repository'; + +export { GitlabUser } from './repository-lib/gitlab/gitlab-user'; + +export { GithubUser } from './repository-lib/github/github-user'; + +export { GiteeUser } from './repository-lib/gitee/gitee-user'; diff --git a/src/repository-lib/base/base-repository.ts b/src/repository-lib/base/base-repository.ts index 49b5b5a..797b9c2 100644 --- a/src/repository-lib/base/base-repository.ts +++ b/src/repository-lib/base/base-repository.ts @@ -1,4 +1,4 @@ -import { BaseRequest } from "../../request-lib"; +import { BaseRequest } from "../../request-lib/base/base-request"; import { Author } from "./author"; import { BaseComment } from "./base-comment"; import { BaseModel } from "./base-model"; diff --git a/src/repository-lib/gitee/gitee-repository.ts b/src/repository-lib/gitee/gitee-repository.ts index eff7031..6422686 100644 --- a/src/repository-lib/gitee/gitee-repository.ts +++ b/src/repository-lib/gitee/gitee-repository.ts @@ -1,4 +1,4 @@ -import { BaseRequest } from "../../request-lib"; +import { BaseRequest } from "../../request-lib/base/base-request"; import { Author } from "../base/author"; import { BaseComment } from "../base/base-comment"; import { BaseModel } from "../base/base-model"; diff --git a/src/repository-lib/github/github-repository.ts b/src/repository-lib/github/github-repository.ts index 1673e32..d9441a9 100644 --- a/src/repository-lib/github/github-repository.ts +++ b/src/repository-lib/github/github-repository.ts @@ -1,4 +1,4 @@ -import { BaseRequest } from "../../request-lib"; +import { BaseRequest } from "../../request-lib/base/base-request"; import { Author } from "../base/author"; import { BaseComment } from "../base/base-comment"; import { BaseModel } from "../base/base-model"; diff --git a/src/repository-lib/gitlab/gitlab-repository.ts b/src/repository-lib/gitlab/gitlab-repository.ts index 51de6ef..368bd5f 100644 --- a/src/repository-lib/gitlab/gitlab-repository.ts +++ b/src/repository-lib/gitlab/gitlab-repository.ts @@ -1,4 +1,4 @@ -import { BaseRequest } from "../../request-lib"; +import { BaseRequest } from "../../request-lib/base/base-request"; import { BaseComment } from "../base/base-comment"; import { BaseModel } from "../base/base-model"; import { BaseRepository } from "../base/base-repository"; diff --git a/src/repository-lib/index.ts b/src/repository-lib/index.ts deleted file mode 100644 index 59aa21f..0000000 --- a/src/repository-lib/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -export { BaseModel } from './base/base-model'; -export { PlainObject } from './base/plain-object'; -/** Gitee */ -export { GiteeUser } from './gitee/gitee-user'; -export { GiteeOptions } from './gitee/gitee-options'; -export { GiteeRepository } from './gitee/gitee-repository'; -/** Github */ -export { GithubUser } from './github/github-user'; -export { GithubOptions } from './github/github-options'; -export { GithubRepository } from './github/github-repository'; -/** Gitlab */ -export { GitlabUser } from './gitlab/gitlab-user'; -export { GitlabOptions } from './gitlab/gitlab-options'; -export { GitlabRepository } from './gitlab/gitlab-repository'; \ No newline at end of file diff --git a/src/request-lib/base/base-request.ts b/src/request-lib/base/base-request.ts index 6faaf28..99b3016 100644 --- a/src/request-lib/base/base-request.ts +++ b/src/request-lib/base/base-request.ts @@ -1,5 +1,7 @@ import { StoragePlatform } from "../../enums"; -import { GiteeUser, GithubUser, GitlabUser } from "../../repository-lib"; +import { GiteeUser } from '../../repository-lib/gitee/gitee-user'; +import { GithubUser } from '../../repository-lib/github/github-user'; +import { GitlabUser } from '../../repository-lib/gitlab/gitlab-user'; import { RequestMethods } from "./request-methods"; import { RequestOptions } from "./request-options"; diff --git a/src/request-lib/base/request-options.ts b/src/request-lib/base/request-options.ts index e022b62..3e1594a 100644 --- a/src/request-lib/base/request-options.ts +++ b/src/request-lib/base/request-options.ts @@ -1,4 +1,6 @@ -import { GiteeOptions, GithubOptions, GitlabOptions } from "../../repository-lib"; +import { GiteeOptions } from "../../repository-lib/gitee/gitee-options"; +import { GithubOptions } from "../../repository-lib/github/github-options"; +import { GitlabOptions } from "../../repository-lib/gitlab/gitlab-options"; import { AxiosOptions } from "../axios/axios-options"; import { WxOptions } from "../wx/wx-options"; diff --git a/src/request-lib/index.ts b/src/request-lib/index.ts deleted file mode 100644 index d80be83..0000000 --- a/src/request-lib/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export {createRequest} from './create-request'; -export {BaseRequest} from './base/base-request'; -export {WxRequestOptions} from './wx/wx-interface'; \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts deleted file mode 100644 index 299cdb5..0000000 --- a/src/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SingletonFactory } from './singleton-factory'; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 815d454..fb2769c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,8 +28,8 @@ "module": "ESNext" /* Specify what module code is generated. */, // "rootDir": "./", /* Specify the root folder within your source files. */ "moduleResolution": "Node" /* Specify how TypeScript looks up a file from a given module specifier. */, - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "baseUrl": "./" /* Specify the base directory to resolve non-relative module names. */, + // "paths": {} /* Specify a set of entries that re-map imports to additional lookup locations. */, // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */ @@ -50,6 +50,7 @@ /* Emit */ "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + "declarationDir": "./dist/types", // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */