XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

ci: run self tests as part of verify workflow (#4364)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/4364 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

1498902c
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

7 个文件 +125 -85
Modified .github/workflows/verify.yml +2 -1
@@ -15,7 +15,8 @@ jobs:
15 15 node-version: "20.19.0"
16 16 - run: npm ci --no-audit
17 17 - run: cp config-vanilla.json config.json
18 - run: npm run verify
18 - run: npm run build
19 - run: npm run start -- --test
19 20 - run: npm run lint:ci
20 21 - run: npm run knip
21 22 - run: npm run prettier
Modified src/controllers/api/artifactTransmutationController.ts +6 -1
@@ -201,11 +201,16 @@ const specialModSets: string[][] = [
201 201 ]
202 202 ];
203 203
204 export const selfTestTransmutation = (): void => {
204 export const selfTestTransmutation = (): boolean => {
205 let allGood = true;
206
205 207 // Ensure we don't error during the .filter logic above.
206 208 for (const { Item } of ExportBoosterPacks["/Lotus/Types/BoosterPacks/ModFuserResult"].components) {
207 209 if (!getUpgrade(Item)) {
208 210 logger.warn(`Transmutation result is not a known upgrade: ${Item}`);
211 allGood = false;
209 212 }
210 213 }
214
215 return allGood;
211 216 };
Modified src/controllers/api/guildTechController.ts +8 -1
@@ -578,16 +578,23 @@ const finishComponentRepair = (
578 578 return inventoryChanges;
579 579 };
580 580
581 export const selfTestGuildTech = (): void => {
581 export const selfTestGuildTech = (): boolean => {
582 let allGood = true;
583
582 584 if (isPersonalResearch("/Lotus/Types/Items/ShipFeatureItems/Railjack/RailjackHullFeatureItemBlueprint") !== true) {
583 585 logger.warn(`isPersonalResearch self-test failed for RailjackHullFeatureItemBlueprint`);
586 allGood = false;
584 587 }
585 588
586 589 if (isPersonalResearch("/Lotus/Types/Recipes/Railjack/Weapons/MissileLauncherEMPTierABlueprint") !== false) {
587 590 logger.warn(`isPersonalResearch self-test failed for MissileLauncherEMPTierABlueprint`);
591 allGood = false;
588 592 }
589 593
590 594 if (isPersonalResearch("I am not a recipe") !== null) {
591 595 logger.warn(`isPersonalResearch self-test failed for non-existent recipe`);
596 allGood = false;
592 597 }
598
599 return allGood;
593 600 };
Modified src/helpers/commandLineArguments.ts +5 -0
@@ -1,5 +1,6 @@
1 1 interface IArguments {
2 2 configPath?: string;
3 test?: boolean;
3 4 dev?: boolean;
4 5 secret?: string;
5 6 docker?: boolean;
@@ -13,6 +14,10 @@ for (let i = 2; i < process.argv.length; ) {
13 14 args.configPath = process.argv[i++];
14 15 break;
15 16
17 case "--test":
18 args.test = true;
19 break;
20
16 21 case "--dev":
17 22 args.dev = true;
18 23 break;
Modified src/index.ts +86 -77
@@ -36,95 +36,104 @@ fs.readFile(path.join(repoDir, "BUILD_DATE"), "utf-8", (err, data) => {
36 36 }
37 37 });
38 38
39 let mongodUri = config.database;
40 if (typeof mongodUri != "string") {
41 const dataDir = path.resolve(mongodUri.dbPath);
42 fs.mkdirSync(dataDir, { recursive: true });
43
44 const downloadDir = "node_modules/.cache";
39 if (args.test) {
40 const allGood = runSelfTests();
41 logger.info(`Self-tests finished`);
42 process.exit(allGood ? 0 : 1);
43 } else {
44 let mongodUri = config.database;
45 if (typeof mongodUri != "string") {
46 const dataDir = path.resolve(mongodUri.dbPath);
47 fs.mkdirSync(dataDir, { recursive: true });
45 48
46 // Breaking MongoMemoryServer is as easy as pressing Ctrl+C while it's extracting and it has no "cache integrity checking" of its own (https://github.com/typegoose/mongodb-memory-server/issues/991)
47 if (fs.existsSync(`${downloadDir}/mongod-x64-win32-7.0.34.exe`)) {
48 if (fs.statSync(`${downloadDir}/mongod-x64-win32-7.0.34.exe`).size != 65191936) {
49 logger.debug(`${downloadDir}/mongod-x64-win32-7.0.34.exe has invalid size, deleting it`);
50 fs.unlinkSync(`${downloadDir}/mongod-x64-win32-7.0.34.exe`);
51 }
52 }
49 const downloadDir = "node_modules/.cache";
53 50
54 const mongod = await MongoMemoryServer.create({
55 binary: {
56 version: mongodUri.engine == "MongoDB 8.0" ? undefined : "7.0.34", // Check https://www.mongodb.com/docs/v7.0/release-notes/7.0/ for updates :)
57 downloadDir
58 },
59 instance: {
60 dbPath: dataDir,
61 port: 27117, // Prefer 27117 just to have a steady port for Compass, etc.
62 portGeneration: true // If 27117 is not available, another port is fine.
51 // Breaking MongoMemoryServer is as easy as pressing Ctrl+C while it's extracting and it has no "cache integrity checking" of its own (https://github.com/typegoose/mongodb-memory-server/issues/991)
52 if (fs.existsSync(`${downloadDir}/mongod-x64-win32-7.0.34.exe`)) {
53 if (fs.statSync(`${downloadDir}/mongod-x64-win32-7.0.34.exe`).size != 65191936) {
54 logger.debug(`${downloadDir}/mongod-x64-win32-7.0.34.exe has invalid size, deleting it`);
55 fs.unlinkSync(`${downloadDir}/mongod-x64-win32-7.0.34.exe`);
56 }
63 57 }
64 });
65 mongodUri = mongod.getUri();
66 logger.info(`MongoDB server running at ${mongodUri}`);
67 mongodUri += "openWF";
68 }
69 58
70 try {
71 await mongoose.connect(mongodUri);
72 } catch (error) {
73 if (error instanceof Error) {
74 logger.error(`Error connecting to MongoDB server: ${error.message}`);
59 const mongod = await MongoMemoryServer.create({
60 binary: {
61 version: mongodUri.engine == "MongoDB 8.0" ? undefined : "7.0.34", // Check https://www.mongodb.com/docs/v7.0/release-notes/7.0/ for updates :)
62 downloadDir
63 },
64 instance: {
65 dbPath: dataDir,
66 port: 27117, // Prefer 27117 just to have a steady port for Compass, etc.
67 portGeneration: true // If 27117 is not available, another port is fine.
68 }
69 });
70 mongodUri = mongod.getUri();
71 logger.info(`MongoDB server running at ${mongodUri}`);
72 mongodUri += "openWF";
75 73 }
76 process.exit(1);
77 }
78 const mongodbBuildInfo = await mongoose.connection.db!.admin().buildInfo();
79 if (Array.isArray(mongodbBuildInfo.versionArray) && mongodbBuildInfo.versionArray.every(x => typeof x == "number")) {
80 const [major, minor, patch] = mongodbBuildInfo.versionArray;
81 logger.info(`Connected to MongoDB v${major}.${minor}.${patch}`);
82 } else {
83 logger.info("Connected to MongoDB (version unknown)");
84 }
85 syncConfigWithDatabase();
86 74
87 try {
88 await startWebServer();
89 } catch (_err) {
90 const err = _err as IListenError;
91 if (err.port) {
92 logger.error(`Failed to bind port ${err.port}`);
93 if (process.platform == "win32") {
94 logger.error(
95 `You can check who has that port via powershell: Get-Process -Id (Get-NetTCPConnection -LocalPort ${err.port}).OwningProcess`
96 );
75 try {
76 await mongoose.connect(mongodUri);
77 } catch (error) {
78 if (error instanceof Error) {
79 logger.error(`Error connecting to MongoDB server: ${error.message}`);
97 80 }
81 process.exit(1);
82 }
83 const mongodbBuildInfo = await mongoose.connection.db!.admin().buildInfo();
84 if (
85 Array.isArray(mongodbBuildInfo.versionArray) &&
86 mongodbBuildInfo.versionArray.every(x => typeof x == "number")
87 ) {
88 const [major, minor, patch] = mongodbBuildInfo.versionArray;
89 logger.info(`Connected to MongoDB v${major}.${minor}.${patch}`);
98 90 } else {
99 logger.error(err.message);
91 logger.info("Connected to MongoDB (version unknown)");
100 92 }
101 process.exit(1);
102 }
93 syncConfigWithDatabase();
103 94
104 for (const [what, key] of [
105 ["IRC", "ircExecutable"],
106 ["HUB", "hubExecutable"]
107 ] as const) {
108 if (config[key]) {
109 logger.info(`Starting ${what}: ${config[key]}`);
110 child_process.execFile(config[key], (error, _stdout, _stderr) => {
111 if (error) {
112 logger.warn(`Failed to start ${what} server`, error);
113 } else {
114 logger.warn(`${what} server terminated unexpectedly`);
95 try {
96 await startWebServer();
97 } catch (_err) {
98 const err = _err as IListenError;
99 if (err.port) {
100 logger.error(`Failed to bind port ${err.port}`);
101 if (process.platform == "win32") {
102 logger.error(
103 `You can check who has that port via powershell: Get-Process -Id (Get-NetTCPConnection -LocalPort ${err.port}).OwningProcess`
104 );
115 105 }
116 });
106 } else {
107 logger.error(err.message);
108 }
109 process.exit(1);
117 110 }
118 }
119 111
120 if (args.dev) {
121 logger.info(
122 "Developer mode is enabled. Note that this project is where it is now due to code contributions; please pay it forward with pull requests."
123 );
124 runSelfTests();
125 }
112 for (const [what, key] of [
113 ["IRC", "ircExecutable"],
114 ["HUB", "hubExecutable"]
115 ] as const) {
116 if (config[key]) {
117 logger.info(`Starting ${what}: ${config[key]}`);
118 child_process.execFile(config[key], (error, _stdout, _stderr) => {
119 if (error) {
120 logger.warn(`Failed to start ${what} server`, error);
121 } else {
122 logger.warn(`${what} server terminated unexpectedly`);
123 }
124 });
125 }
126 }
127
128 if (args.dev) {
129 logger.info(
130 "Developer mode is enabled. Note that this project is where it is now due to code contributions; please pay it forward with pull requests."
131 );
132 runSelfTests();
133 }
126 134
127 void updateWorldStateCollections();
128 setInterval(() => {
129 135 void updateWorldStateCollections();
130 }, 60_000);
136 setInterval(() => {
137 void updateWorldStateCollections();
138 }, 60_000);
139 }
Modified src/services/selfTestService.ts +6 -4
@@ -2,8 +2,10 @@ import { selfTestTransmutation } from "../controllers/api/artifactTransmutationC
2 2 import { selfTestGuildTech } from "../controllers/api/guildTechController.ts";
3 3 import { selfTestServersideVendors } from "./serversideVendorsService.ts";
4 4
5 export const runSelfTests = (): void => {
6 selfTestServersideVendors();
7 selfTestGuildTech();
8 selfTestTransmutation();
5 export const runSelfTests = (): boolean => {
6 let allGood = true;
7 allGood &&= selfTestServersideVendors();
8 allGood &&= selfTestGuildTech();
9 allGood &&= selfTestTransmutation();
10 return allGood;
9 11 };
Modified src/services/serversideVendorsService.ts +12 -1
@@ -509,12 +509,15 @@ const getLegacyNightwaveManifestType = (buildLabel: string): string | undefined
509 509 return season >= 0 && season < manifests.length ? manifests[season] : undefined;
510 510 };
511 511
512 export const selfTestServersideVendors = (): void => {
512 export const selfTestServersideVendors = (): boolean => {
513 let allGood = true;
514
513 515 if (
514 516 getCycleDuration(ExportVendors["/Lotus/Types/Game/VendorManifests/Hubs/TeshinHardModeVendorManifest"]) !=
515 517 unixTimesInMs.week
516 518 ) {
517 519 logger.warn(`getCycleDuration self test failed`);
520 allGood = false;
518 521 }
519 522
520 523 for (let i = 0; i != 2; ++i) {
@@ -535,6 +538,7 @@ export const selfTestServersideVendors = (): void => {
535 538 logger.warn(
536 539 `self test failed for /Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest with fullStock=${fullStock}`
537 540 );
541 allGood = false;
538 542 }
539 543 }
540 544
@@ -551,6 +555,7 @@ export const selfTestServersideVendors = (): void => {
551 555 cms.reduce((a, x) => a + (x.Bin == "BIN_0" ? 1 : 0), 0) < 4
552 556 ) {
553 557 logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Hubs/RailjackCrewMemberVendorManifest`);
558 allGood = false;
554 559 }
555 560
556 561 const temple = getVendorManifestByTypeName(
@@ -559,6 +564,7 @@ export const selfTestServersideVendors = (): void => {
559 564 )!.VendorInfo.ItemManifest;
560 565 if (!temple.find(x => x.StoreItem == "/Lotus/StoreItems/Types/Items/MiscItems/Kuva")) {
561 566 logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/TheHex/Temple1999VendorManifest`);
567 allGood = false;
562 568 }
563 569
564 570 const nakak = getVendorManifestByTypeName("/Lotus/Types/Game/VendorManifests/Ostron/MaskSalesmanManifest", false)!
@@ -575,6 +581,7 @@ export const selfTestServersideVendors = (): void => {
575 581 // The remaining offers should be computed by weighted RNG.
576 582 ) {
577 583 logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Ostron/MaskSalesmanManifest`);
584 allGood = false;
578 585 }
579 586
580 587 // strange case where numItems is 5 even tho only 3 offers can possibly be generated
@@ -584,6 +591,7 @@ export const selfTestServersideVendors = (): void => {
584 591 )!.VendorInfo.ItemManifest;
585 592 if (loid.length != 3) {
586 593 logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/EntratiLabs/EntratiLabsCommisionsManifest`);
594 allGood = false;
587 595 }
588 596
589 597 // This should not produce an infinite loop.
@@ -597,5 +605,8 @@ export const selfTestServersideVendors = (): void => {
597 605 .ItemManifest.length > 200
598 606 ) {
599 607 logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Duviri/AcrithisVendorManifest`);
608 allGood = false;
600 609 }
610
611 return allGood;
601 612 };