返回提交历史
Modified
.github/workflows/verify.yml
+2
-1
Modified
src/controllers/api/artifactTransmutationController.ts
+6
-1
Modified
src/controllers/api/guildTechController.ts
+8
-1
Modified
src/helpers/commandLineArguments.ts
+5
-0
Modified
src/index.ts
+86
-77
Modified
src/services/selfTestService.ts
+6
-4
Modified
src/services/serversideVendorsService.ts
+12
-1
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
代码差异
7 个文件
+125
-85
@@ -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
@@ -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
};
@@ -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
};
@@ -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;
@@ -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
}
@@ -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
};
@@ -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
};