chore: add e2e tests

This commit is contained in:
Aaron Ji 2025-04-09 16:35:10 +08:00
parent 6f657ae19a
commit 3f85fc4080
6 changed files with 481 additions and 33 deletions

212
package-lock.json generated
View File

@ -54,6 +54,7 @@
"@types/bcrypt": "^5.0.0",
"@types/busboy": "^1.5.4",
"@types/cors": "^2.8.17",
"@types/jest": "^29.5.14",
"@types/koa": "^2.15.0",
"@types/koa-compress": "^4.0.6",
"@types/node": "^20.14.13",
@ -67,6 +68,7 @@
"firebase-functions-test": "^3.0.0",
"pino-pretty": "^13.0.0",
"replicate": "^0.16.1",
"ts-jest": "^29.3.1",
"typescript": "^5.5.4"
},
"engines": {
@ -1355,7 +1357,6 @@
"resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
"integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
"dev": true,
"peer": true,
"dependencies": {
"jest-get-type": "^29.6.3"
},
@ -1467,7 +1468,6 @@
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
"integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
"dev": true,
"peer": true,
"dependencies": {
"@sinclair/typebox": "^0.27.8"
},
@ -1554,7 +1554,6 @@
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
"dev": true,
"peer": true,
"dependencies": {
"@jest/schemas": "^29.6.3",
"@types/istanbul-lib-coverage": "^2.0.0",
@ -2207,8 +2206,7 @@
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
"integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/@sinonjs/commons": {
"version": "3.0.1",
@ -2433,15 +2431,13 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
"integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/@types/istanbul-lib-report": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
"integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
"dev": true,
"peer": true,
"dependencies": {
"@types/istanbul-lib-coverage": "*"
}
@ -2451,11 +2447,21 @@
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
"integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
"dev": true,
"peer": true,
"dependencies": {
"@types/istanbul-lib-report": "*"
}
},
"node_modules/@types/jest": {
"version": "29.5.14",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz",
"integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"expect": "^29.0.0",
"pretty-format": "^29.0.0"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -2640,8 +2646,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/@types/tough-cookie": {
"version": "4.0.5",
@ -2679,7 +2684,6 @@
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
"dev": true,
"peer": true,
"dependencies": {
"@types/yargs-parser": "*"
}
@ -2688,8 +2692,7 @@
"version": "21.0.3",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
"integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/@types/yauzl": {
"version": "2.10.3",
@ -3716,6 +3719,19 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/bs-logger": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-json-stable-stringify": "2.x"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/bser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@ -4010,7 +4026,6 @@
"url": "https://github.com/sponsors/sibiraj-s"
}
],
"peer": true,
"engines": {
"node": ">=8"
}
@ -4702,7 +4717,6 @@
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
"integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
"dev": true,
"peer": true,
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
@ -4822,6 +4836,22 @@
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/ejs": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"jake": "^10.8.5"
},
"bin": {
"ejs": "bin/cli.js"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.735",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.735.tgz",
@ -5478,7 +5508,6 @@
"resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
"integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
"dev": true,
"peer": true,
"dependencies": {
"@jest/expect-utils": "^29.7.0",
"jest-get-type": "^29.6.3",
@ -5762,6 +5791,39 @@
"node": "^10.12.0 || >=12.0.0"
}
},
"node_modules/filelist": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"minimatch": "^5.0.1"
}
},
"node_modules/filelist/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/filelist/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@ -7415,6 +7477,25 @@
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/jake": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
"integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"async": "^3.2.3",
"chalk": "^4.0.2",
"filelist": "^1.0.4",
"minimatch": "^3.1.2"
},
"bin": {
"jake": "bin/cli.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
@ -7595,7 +7676,6 @@
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
"integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
"dev": true,
"peer": true,
"dependencies": {
"chalk": "^4.0.0",
"diff-sequences": "^29.6.3",
@ -7659,7 +7739,6 @@
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
"integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
"dev": true,
"peer": true,
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
@ -7709,7 +7788,6 @@
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
"integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
"dev": true,
"peer": true,
"dependencies": {
"chalk": "^4.0.0",
"jest-diff": "^29.7.0",
@ -7725,7 +7803,6 @@
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
"integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
"dev": true,
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.12.13",
"@jest/types": "^29.6.3",
@ -7944,7 +8021,6 @@
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
"dev": true,
"peer": true,
"dependencies": {
"@jest/types": "^29.6.3",
"@types/node": "*",
@ -8142,7 +8218,6 @@
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"peer": true,
"bin": {
"json5": "lib/cli.js"
},
@ -8623,6 +8698,13 @@
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -8707,6 +8789,13 @@
"semver": "bin/semver.js"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true,
"license": "ISC"
},
"node_modules/make-fetch-happen": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz",
@ -10463,7 +10552,6 @@
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
"dev": true,
"peer": true,
"dependencies": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
@ -10478,7 +10566,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=10"
},
@ -10967,8 +11054,7 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/readable-stream": {
"version": "3.6.2",
@ -11299,9 +11385,10 @@
"license": "BSD-3-Clause"
},
"node_modules/semver": {
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
@ -11735,7 +11822,6 @@
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
"integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
"dev": true,
"peer": true,
"dependencies": {
"escape-string-regexp": "^2.0.0"
},
@ -11748,7 +11834,6 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
"integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
"dev": true,
"peer": true,
"engines": {
"node": ">=8"
}
@ -12226,6 +12311,69 @@
"integrity": "sha512-3phiGcxPSSR47RBubQxPoZ+pqXsEsozLo4G4AlSrsMKTFg9TA3l+3he5BqpUi9wiuDbaHWXH/amlzQ49uEdXtg==",
"dev": true
},
"node_modules/ts-jest": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.1.tgz",
"integrity": "sha512-FT2PIRtZABwl6+ZCry8IY7JZ3xMuppsEV9qFVHOVe8jDzggwUZ9TsM4chyJxL9yi6LvkqcZYU3LmapEE454zBQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"bs-logger": "^0.2.6",
"ejs": "^3.1.10",
"fast-json-stable-stringify": "^2.1.0",
"jest-util": "^29.0.0",
"json5": "^2.2.3",
"lodash.memoize": "^4.1.2",
"make-error": "^1.3.6",
"semver": "^7.7.1",
"type-fest": "^4.38.0",
"yargs-parser": "^21.1.1"
},
"bin": {
"ts-jest": "cli.js"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
},
"peerDependencies": {
"@babel/core": ">=7.0.0-beta.0 <8",
"@jest/transform": "^29.0.0",
"@jest/types": "^29.0.0",
"babel-jest": "^29.0.0",
"jest": "^29.0.0",
"typescript": ">=4.3 <6"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"@jest/transform": {
"optional": true
},
"@jest/types": {
"optional": true
},
"babel-jest": {
"optional": true
},
"esbuild": {
"optional": true
}
}
},
"node_modules/ts-jest/node_modules/type-fest": {
"version": "4.39.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz",
"integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",

View File

@ -8,7 +8,8 @@
"serve": "npm run build && npm run start",
"debug": "npm run build && npm run dev",
"start": "node ./build/stand-alone/crawl.js",
"dry-run": "NODE_ENV=dry-run node ./build/stand-alone/search.js"
"dry-run": "NODE_ENV=dry-run node ./build/stand-alone/search.js",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --config ./test/jest.config.js"
},
"engines": {
"node": ">=18"
@ -63,6 +64,7 @@
"@types/bcrypt": "^5.0.0",
"@types/busboy": "^1.5.4",
"@types/cors": "^2.8.17",
"@types/jest": "^29.5.14",
"@types/koa": "^2.15.0",
"@types/koa-compress": "^4.0.6",
"@types/node": "^20.14.13",
@ -76,6 +78,7 @@
"firebase-functions-test": "^3.0.0",
"pino-pretty": "^13.0.0",
"replicate": "^0.16.1",
"ts-jest": "^29.3.1",
"typescript": "^5.5.4"
},
"private": true,

182
test/e2e/search.test.ts Normal file
View File

@ -0,0 +1,182 @@
import { TestHttpClient } from '../utils/http-client';
function calculateToken(data: any[], chargeAmountScaler: number) {
const length = data.length;
return 10000 * Math.ceil(length / 10) * chargeAmountScaler;
}
describe("Test normal cases", () => {
let httpClient: TestHttpClient;
let baseUri = 'http://localhost:3001';
const apiKey = process.env.TEST_API_KEY;
beforeAll(async() => {
expect(apiKey).toBeDefined();
httpClient = new TestHttpClient(apiKey!, baseUri);
});
afterAll(async() => {
await httpClient.close();
});
it('[without api key]', async() => {
const client = new TestHttpClient('', baseUri);
try {
await client.search({q: 'Jina AI'}, {
headers: {
'x-no-cache': 'true',
'X-Respond-With': 'no-content',
'Accept': 'application/json',
}
});
} catch (e) {
expect(e).toBeDefined();
expect(e.code).toEqual(401);
expect(e.message).toEqual('Invalid API key, please get a new one from https://jina.ai');
}
});
/**
* @description
* Test:
* - no content
* - no fallback
* - json response
* - no links
* - no images
*/
fit("[without content][without fallback][json response]", async () => {
const result = await httpClient.search({q: 'Jina AI'}, {
headers: {
'x-no-cache': 'true',
'X-Respond-With': 'no-content',
'Accept': 'application/json',
}
});
const data = result.data;
expect(result.code).toBe(200);
expect(data).toBeDefined();
expect(data.length).toBeGreaterThan(0);
const firstMatch = data[0];
expect(firstMatch.title).toBeDefined();
expect(firstMatch.favicon).toBeUndefined();
expect(firstMatch.content).toBeUndefined();
expect(firstMatch.links).toBeUndefined();
expect(firstMatch.images).toBeUndefined();
const tokens = calculateToken(data, 1);
expect(result.meta).toEqual({
usage: {
tokens
}
});
});
/**
* @description
* Test:
* - no content
* - with fallback
* - json response
* - no links
* - no images
*/
it('[without content][with fallback][json response]', async() => {
const result = await httpClient.search({q: 'new Jeans Hanni 人格分析 存在口供偏差'}, {
headers: {
'x-no-cache': 'true',
'X-Respond-With': 'no-content',
'Accept': 'application/json',
}
});
const data = result.data;
expect(result.code).toBe(200);
expect(data).toBeDefined();
const tokens = calculateToken(data, 2);
expect(result.meta).toEqual({
fallback: 'new Jeans Hanni',
usage: {
tokens
}
});
});
/**
* @description
* Test:
* - with content
* - no fallback
* - text response
* - no links
* - no images
*/
it('[with content][without fallback][text response]', async () => {
const result = await httpClient.search({q: 'Jina AI'}, {
headers: {
'x-no-cache': 'true',
'X-Respond-With': 'no-content',
},
responseType: 'text',
});
expect(typeof result).toEqual('string');
// extract 'Title' and 'URL Source'
const titleRegex = /Title/g;
const titles = result.matchAll(titleRegex);
const titleArray = Array.from(titles);
const urlRegex = /URL Source/g;
const urls = result.matchAll(urlRegex);
const urlArray = Array.from(urls);
expect(titleArray.length).toEqual(urlArray.length);
});
/**
* @description
* Test:
* - with content
* - without fallback
* - json response
* - with favicon
* - with links
* - with images
*
*/
it("[with content][with favicon][without fallback][json response]", async () => {
const result = await httpClient.search({q: 'Jina AI'}, {
headers: {
'x-no-cache': 'true',
'Accept': 'application/json',
'X-With-Favicons': 'true',
'X-With-Images-Summary': 'true',
'X-With-Links-Summary': 'true',
}
});
const data = result.data;
expect(result.code).toBe(200);
expect(data).toBeDefined();
expect(data.length).toBeGreaterThan(0);
const firstMatch = data[0];
expect(firstMatch.title).toBeDefined();
expect(firstMatch.favicon).toBeDefined();
expect(firstMatch.content).toBeDefined();
expect(Object.keys(firstMatch.links).length).toBeGreaterThan(0);
expect(Object.keys(firstMatch.images).length).toBeGreaterThan(0);
const tokens = calculateToken(data, 1);
expect(result.meta.usage.tokens).toBeGreaterThan(tokens)
});
});

30
test/jest.config.js Normal file
View File

@ -0,0 +1,30 @@
/** @type {import('ts-jest').JestConfigWithTsJest} **/
module.exports = {
testEnvironment: "node",
verbose: true,
preset: 'ts-jest',
moduleFileExtensions: [
"js",
"ts",
"json",
"node"
],
transform: {
"^.+\\.tsx?$": ["ts-jest",{
tsconfig: "<rootDir>/test/tsconfig.test.json",
}],
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
testMatch: [
"<rootDir>/test/e2e/**/*.test.ts"
],
setupFilesAfterEnv: [],
testPathIgnorePatterns: [
"<rootDir>/node_modules/",
"<rootDir>/dist/",
],
rootDir: '../',
testTimeout: 30000,
};

18
test/tsconfig.test.json Normal file
View File

@ -0,0 +1,18 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"module": "CommonJS",
"strict": true,
"noImplicitAny": false,
"types": ["jest", "node"],
"isolatedModules": true,
"esModuleInterop": true,
"allowJs": true,
"skipLibCheck": true,
"useDefineForClassFields": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["../build/**/*", "./**/*"]
}

67
test/utils/http-client.ts Normal file
View File

@ -0,0 +1,67 @@
export class TestHttpClient {
private controller: AbortController | null = null;
constructor(
public apiKey: string,
public baseUri: string = 'http://localhost:3000'
) {
this.baseUri = baseUri;
}
async request(uri: string, payload: any, options: any = {}) {
this.controller = new AbortController();
const response = await fetch(`${this.baseUri}${uri}`, {
method: options.method || 'GET',
headers: {
Authorization: `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options?.headers,
},
body: JSON.stringify(payload),
signal: this.controller.signal,
});
if (!response.ok) {
let error: any;
try {
error = await response.json();
} catch (e) {
error = await response.text();
}
throw error;
}
if (options.responseType === 'json') {
return await response.json();
}
if (options.responseType === 'text') {
return await response.text();
}
throw new Error('Unsupported response type');
}
async search(payload: any, options: any = {}) {
const data = await this.request('', payload, {
responseType: 'json',
method: 'POST',
...options,
});
return data;
}
// Add this close method to clean up connections
async close() {
if (this.controller) {
this.controller.abort();
this.controller = null;
}
// This helps resolve the open handle issue with Node's fetch
await new Promise(resolve => setTimeout(resolve, 100));
}
}