add test case

This commit is contained in:
liuyi 2025-05-27 14:07:54 +08:00
parent 1cfeb1f22e
commit a6dcfce339
7 changed files with 243 additions and 48 deletions

View File

@ -26,11 +26,11 @@
"@nestjs/platform-fastify": "^10.0.3",
"@nestjs/swagger": "^7.4.2",
"@nestjs/typeorm": "^11.0.0",
"better-sqlite3": "^11.10.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"deepmerge": "^4.3.1",
"lodash": "^4.17.21",
"mysql2": "^3.14.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^5.0.1",
"rxjs": "^7.8.1",

View File

@ -22,10 +22,7 @@ importers:
version: 7.4.2(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)
'@nestjs/typeorm':
specifier: ^11.0.0
version: 11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2)(typeorm@0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)))
better-sqlite3:
specifier: ^11.10.0
version: 11.10.0
version: 11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2)(typeorm@0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)))
class-transformer:
specifier: ^0.5.1
version: 0.5.1
@ -38,6 +35,9 @@ importers:
lodash:
specifier: ^4.17.21
version: 4.17.21
mysql2:
specifier: ^3.14.1
version: 3.14.1
reflect-metadata:
specifier: ^0.1.13
version: 0.1.14
@ -52,7 +52,7 @@ importers:
version: 2.17.0
typeorm:
specifier: ^0.3.24
version: 0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6))
version: 0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6))
validator:
specifier: ^13.15.0
version: 13.15.0
@ -1170,6 +1170,10 @@ packages:
avvio@8.4.0:
resolution: {integrity: sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==}
aws-ssl-profiles@1.1.2:
resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==}
engines: {node: '>= 6.0.0'}
babel-jest@29.7.0:
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@ -1561,6 +1565,10 @@ packages:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
denque@2.1.0:
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
engines: {node: '>=0.10'}
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
@ -2080,6 +2088,9 @@ packages:
functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
generate-function@2.3.1:
resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==}
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
@ -2224,6 +2235,10 @@ packages:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@ -2361,6 +2376,9 @@ packages:
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
engines: {node: '>=0.10.0'}
is-property@1.0.2:
resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==}
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
@ -2686,6 +2704,9 @@ packages:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
engines: {node: '>=10'}
long@5.3.2:
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
lowercase-keys@2.0.0:
resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
engines: {node: '>=8'}
@ -2699,6 +2720,14 @@ packages:
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
lru-cache@7.18.3:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
lru.min@1.1.2:
resolution: {integrity: sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==}
engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'}
magic-string@0.30.8:
resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==}
engines: {node: '>=12'}
@ -2818,6 +2847,14 @@ packages:
resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
mysql2@3.14.1:
resolution: {integrity: sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w==}
engines: {node: '>= 8.0'}
named-placeholders@1.1.3:
resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==}
engines: {node: '>=12.0.0'}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@ -3354,6 +3391,9 @@ packages:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
engines: {node: '>= 0.8.0'}
seq-queue@0.0.5:
resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==}
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
@ -3475,6 +3515,10 @@ packages:
resolution: {integrity: sha512-+fLpbAbWkQ+d0JEchJT/NrRRXbYRNbG15gFpANx73EwxQB1PRjj+k/OI0GTU0J63g8ikGkJECQp9z8XEJZvPRw==}
engines: {node: '>=14'}
sqlstring@2.3.3:
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
engines: {node: '>= 0.6'}
stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
@ -4685,13 +4729,13 @@ snapshots:
optionalDependencies:
'@nestjs/platform-express': 10.4.17(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)
'@nestjs/typeorm@11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2)(typeorm@0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)))':
'@nestjs/typeorm@11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2)(typeorm@0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)))':
dependencies:
'@nestjs/common': 10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2)
'@nestjs/core': 10.4.17(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2)
reflect-metadata: 0.1.14
rxjs: 7.8.2
typeorm: 0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6))
typeorm: 0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6))
'@noble/hashes@1.8.0': {}
@ -5277,6 +5321,8 @@ snapshots:
'@fastify/error': 3.4.1
fastq: 1.19.1
aws-ssl-profiles@1.1.2: {}
babel-jest@29.7.0(@babel/core@7.27.1):
dependencies:
'@babel/core': 7.27.1
@ -5340,6 +5386,7 @@ snapshots:
dependencies:
bindings: 1.5.0
prebuild-install: 7.1.3
optional: true
bin-check@4.1.0:
dependencies:
@ -5362,6 +5409,7 @@ snapshots:
bindings@1.5.0:
dependencies:
file-uri-to-path: 1.0.0
optional: true
bl@4.1.0:
dependencies:
@ -5495,7 +5543,8 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
chownr@1.1.4: {}
chownr@1.1.4:
optional: true
chrome-trace-event@1.0.4: {}
@ -5686,7 +5735,8 @@ snapshots:
dedent@1.6.0: {}
deep-extend@0.6.0: {}
deep-extend@0.6.0:
optional: true
deep-is@0.1.4: {}
@ -5712,13 +5762,16 @@ snapshots:
delayed-stream@1.0.0: {}
denque@2.1.0: {}
depd@2.0.0:
optional: true
destroy@1.2.0:
optional: true
detect-libc@2.0.4: {}
detect-libc@2.0.4:
optional: true
detect-newline@3.1.0: {}
@ -6105,7 +6158,8 @@ snapshots:
exit@0.1.2: {}
expand-template@2.0.3: {}
expand-template@2.0.3:
optional: true
expect@29.7.0:
dependencies:
@ -6263,7 +6317,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
file-uri-to-path@1.0.0: {}
file-uri-to-path@1.0.0:
optional: true
filename-reserved-regex@3.0.0: {}
@ -6363,7 +6418,8 @@ snapshots:
fresh@0.5.2:
optional: true
fs-constants@1.0.0: {}
fs-constants@1.0.0:
optional: true
fs-extra@10.1.0:
dependencies:
@ -6391,6 +6447,10 @@ snapshots:
functions-have-names@1.2.3: {}
generate-function@2.3.1:
dependencies:
is-property: 1.0.2
gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {}
@ -6429,7 +6489,8 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.3.0
github-from-package@0.0.0: {}
github-from-package@0.0.0:
optional: true
glob-parent@5.1.2:
dependencies:
@ -6554,6 +6615,10 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
ieee754@1.2.1: {}
ignore@5.3.2: {}
@ -6577,7 +6642,8 @@ snapshots:
inherits@2.0.4: {}
ini@1.3.8: {}
ini@1.3.8:
optional: true
inquirer@8.2.6:
dependencies:
@ -6707,6 +6773,8 @@ snapshots:
is-plain-object@5.0.0: {}
is-property@1.0.2: {}
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
@ -7216,6 +7284,8 @@ snapshots:
chalk: 4.1.2
is-unicode-supported: 0.1.0
long@5.3.2: {}
lowercase-keys@2.0.0: {}
lru-cache@10.4.3: {}
@ -7229,6 +7299,10 @@ snapshots:
dependencies:
yallist: 3.1.1
lru-cache@7.18.3: {}
lru.min@1.1.2: {}
magic-string@0.30.8:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
@ -7297,7 +7371,8 @@ snapshots:
minipass@7.1.2: {}
mkdirp-classic@0.5.3: {}
mkdirp-classic@0.5.3:
optional: true
mkdirp@0.5.6:
dependencies:
@ -7328,9 +7403,26 @@ snapshots:
mute-stream@1.0.0: {}
mysql2@3.14.1:
dependencies:
aws-ssl-profiles: 1.1.2
denque: 2.1.0
generate-function: 2.3.1
iconv-lite: 0.6.3
long: 5.3.2
lru.min: 1.1.2
named-placeholders: 1.1.3
seq-queue: 0.0.5
sqlstring: 2.3.3
named-placeholders@1.1.3:
dependencies:
lru-cache: 7.18.3
nanoid@3.3.11: {}
napi-build-utils@2.0.0: {}
napi-build-utils@2.0.0:
optional: true
natural-compare-lite@1.4.0: {}
@ -7344,6 +7436,7 @@ snapshots:
node-abi@3.75.0:
dependencies:
semver: 7.7.1
optional: true
node-abort-controller@3.1.1: {}
@ -7590,6 +7683,7 @@ snapshots:
simple-get: 4.0.1
tar-fs: 2.1.2
tunnel-agent: 0.6.0
optional: true
prelude-ls@1.2.1: {}
@ -7671,6 +7765,7 @@ snapshots:
ini: 1.3.8
minimist: 1.2.8
strip-json-comments: 2.0.1
optional: true
react-is@18.3.1: {}
@ -7879,6 +7974,8 @@ snapshots:
- supports-color
optional: true
seq-queue@0.0.5: {}
serialize-javascript@6.0.2:
dependencies:
randombytes: 2.1.0
@ -7969,13 +8066,15 @@ snapshots:
signal-exit@4.1.0: {}
simple-concat@1.0.1: {}
simple-concat@1.0.1:
optional: true
simple-get@4.0.1:
dependencies:
decompress-response: 6.0.0
once: 1.4.0
simple-concat: 1.0.1
optional: true
sisteransi@1.0.5: {}
@ -8015,6 +8114,8 @@ snapshots:
sql-highlight@6.0.0: {}
sqlstring@2.3.3: {}
stack-utils@2.0.6:
dependencies:
escape-string-regexp: 2.0.0
@ -8090,7 +8191,8 @@ snapshots:
strip-final-newline@2.0.0: {}
strip-json-comments@2.0.1: {}
strip-json-comments@2.0.1:
optional: true
strip-json-comments@3.1.1: {}
@ -8150,6 +8252,7 @@ snapshots:
mkdirp-classic: 0.5.3
pump: 3.0.2
tar-stream: 2.2.0
optional: true
tar-stream@2.2.0:
dependencies:
@ -8158,6 +8261,7 @@ snapshots:
fs-constants: 1.0.0
inherits: 2.0.4
readable-stream: 3.6.2
optional: true
terser-webpack-plugin@5.3.14(@swc/core@1.11.24)(webpack@5.97.1(@swc/core@1.11.24)):
dependencies:
@ -8303,6 +8407,7 @@ snapshots:
tunnel-agent@0.6.0:
dependencies:
safe-buffer: 5.2.1
optional: true
type-check@0.4.0:
dependencies:
@ -8356,7 +8461,7 @@ snapshots:
typedarray@0.0.6:
optional: true
typeorm@0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)):
typeorm@0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)):
dependencies:
'@sqltools/formatter': 1.2.5
ansis: 3.17.0
@ -8375,6 +8480,7 @@ snapshots:
yargs: 17.7.2
optionalDependencies:
better-sqlite3: 11.10.0
mysql2: 3.14.1
ts-node: 10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)
transitivePeerDependencies:
- babel-plugin-macros

View File

@ -11,4 +11,5 @@ export const database = (): TypeOrmModuleOptions => ({
database: '3r',
synchronize: true,
autoLoadEntities: true,
timezone: '+08:00',
});

View File

@ -6,6 +6,7 @@ import {
Entity,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
PrimaryColumn,
Relation,
@ -67,7 +68,7 @@ export class PostEntity extends BaseEntity {
commentCount: number;
@Expose()
@OneToMany(() => CategoryEntity, (category) => category.posts, {
@ManyToOne(() => CategoryEntity, (category) => category.posts, {
nullable: true,
onDelete: 'SET NULL',
})

View File

@ -10,11 +10,9 @@ import {
QueryCommentTreeDto,
} from '@/modules/content/dtos/comment.dto';
import { CommentEntity } from '@/modules/content/entities/comment.entity';
import { CommentRepository, PostRepository } from '@/modules/content/repositories';
import { treePaginate } from '@/modules/database/utils';
import { CommentRepository } from '../repositories/comment.repository';
import { PostRepository } from '../repositories/post.repository';
@Injectable()
export class CommentService {
constructor(
@ -82,7 +80,7 @@ export class CommentService {
}
parent = await this.repository.findOne({
where: { id },
relations: ['parent', 'children'],
relations: ['parent', 'children', 'post'],
});
if (!parent) {
throw new EntityNotFoundError(CommentEntity, `Parent Comment ${id} not found`);

View File

@ -3,26 +3,20 @@ import { describe } from 'node:test';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { Test, TestingModule } from '@nestjs/testing';
import { omit, pick } from 'lodash';
import { pick } from 'lodash';
import { DataSource } from 'typeorm';
import { database } from '@/config';
import { ContentModule } from '@/modules/content/content.module';
import { CategoryEntity, CommentEntity, PostEntity, TagEntity } from '@/modules/content/entities';
import { CategoryRepository, PostRepository, TagRepository } from '@/modules/content/repositories';
import { DatabaseModule } from '@/modules/database/database.module';
import { CommentRepository } from '../src/modules/content/repositories/comment.repository';
import { generateRandomNumber, generateUniqueRandomNumbers } from './generate-mock-data';
import { commentData, INIT_DATA, initialCategories, postData, tagData } from './test-data';
describe('category test', () => {
let datasource: DataSource;
let app: NestFastifyApplication;
let categoryRepository: CategoryRepository;
let tagRepository: TagRepository;
let postRepository: PostRepository;
let commentRepository: CommentRepository;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
@ -32,16 +26,31 @@ describe('category test', () => {
await app.init();
await app.getHttpAdapter().getInstance().ready();
categoryRepository = module.get<CategoryRepository>(CategoryRepository);
tagRepository = module.get<TagRepository>(TagRepository);
postRepository = module.get<PostRepository>(PostRepository);
commentRepository = module.get<CommentRepository>(CommentRepository);
datasource = module.get<DataSource>(DataSource);
if (!datasource.isInitialized) {
await datasource.initialize();
}
});
beforeEach(async () => {
if (INIT_DATA) {
await categoryRepository.deleteAll();
await postRepository.deleteAll();
await tagRepository.deleteAll();
await commentRepository.deleteAll();
const queryRunner = datasource.createQueryRunner();
try {
await queryRunner.query('SET FOREIGN_KEY_CHECKS = 0');
datasource.entityMetadatas.map(async (entity) => {
const table = entity.schema
? `${entity.schema}.${entity.tableName}`
: `${entity.tableName}`;
console.log(`TRUNCATE TABLE ${table}`);
await queryRunner.query(`TRUNCATE TABLE ${table}`);
return table;
});
// await queryRunner.query(`TRUNCATE TABLE ${tables}`);
} finally {
await queryRunner.query('SET FOREIGN_KEY_CHECKS = 1');
await queryRunner.release();
}
// init category data
const categories = await addCategory(app, initialCategories);
@ -50,14 +59,21 @@ describe('category test', () => {
const tags = await addTag(app, tagData);
console.log(tags);
// init post data
addPost(
const posts = await addPost(
app,
postData,
tags.map((tag) => tag.id),
categories.map((category) => category.id),
);
console.log(posts);
console.log('='.repeat(100));
// init comment data
addComment(app, commentData);
const comments = await addComment(
app,
commentData,
posts.map((post) => post.id),
);
console.log(comments);
}
});
@ -120,11 +136,13 @@ async function addPost(
if (app && data && data.length > 0) {
for (let index = 0; index < data.length; index++) {
const item = data[index];
// TODO add tag and category
item.category = categories[generateRandomNumber(1, categories.length - 1)[0]];
item.tags = generateUniqueRandomNumbers(0, tags.length - 1, 3).map((idx) => tags[idx]);
// console.log(JSON.stringify(item));
const result = await app.inject({
method: 'POST',
url: '/post',
body: omit(item, ['tags', 'category']),
url: '/posts',
body: item,
});
const addedItem: PostEntity = result.json();
posts.push(addedItem);
@ -136,17 +154,32 @@ async function addPost(
async function addComment(
app: NestFastifyApplication,
data: RecordAny[],
posts: string[],
): Promise<CommentEntity[]> {
const comments: CommentEntity[] = [];
if (app && data && data.length > 0) {
for (let index = 0; index < data.length; index++) {
const item = data[index];
item.post = posts[generateRandomNumber(0, posts.length - 1)[0]];
const commentsFilter = comments
.filter((comment) => comment.post === item.post)
.map((comment) => comment.id);
console.log('A'.repeat(100));
console.log(commentsFilter);
item.parent =
commentsFilter.length > 0
? commentsFilter[generateRandomNumber(0, commentsFilter.length - 1)[0]]
: undefined;
console.log(JSON.stringify(item));
const result = await app.inject({
method: 'POST',
url: '/comment',
body: item,
});
const addedItem: CommentEntity = result.json();
const addedItem = result.json();
console.log(addedItem);
addedItem.post = item.post;
comments.push(addedItem);
}
}

View File

@ -35,3 +35,59 @@ export function generateMockComment(): CreateCommentDto {
parent: fakerEN.string.uuid(),
};
}
/**
*
* @param start
* @param end
* @param n
* @returns n [start, end]
* @throws
*/
export function generateUniqueRandomNumbers(start: number, end: number, n: number): number[] {
// 处理反向范围并计算实际区间
const [min, max] = start <= end ? [start, end] : [end, start];
const range = max - min + 1;
if (n <= 0 || !Number.isInteger(n)) {
throw new Error('参数 n 必须是正整数');
}
if (n === 1) {
return generateRandomNumber(start, end);
}
// 参数校验:请求数量不能超过可用唯一值总数
if (n > range) {
throw new Error(
`Cannot generate ${n} unique numbers in range [${min}, ${max}]. Maximum possible: ${range}`,
);
}
// 生成所有可能的候选数字
const candidates = Array.from({ length: range }, (_, i) => min + i);
// Fisher-Yates 洗牌算法随机打乱数组
for (let i = candidates.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[candidates[i], candidates[j]] = [candidates[j], candidates[i]];
}
// 返回前 n 个元素
return candidates.slice(0, n);
}
export function generateRandomNumber(start: number, end: number): number[] {
// 处理反向范围(确保 min <= max
const [min, max] = start <= end ? [start, end] : [end, start];
if (min === max) {
return [min];
}
// 生成 1 个随机整数
return Array.from({ length: 1 }, () => {
// 随机数公式Math.floor(Math.random() * (max - min + 1)) + min
return Math.floor(Math.random() * (max - min + 1)) + min;
});
}