@vlydev/cs2-masked-inspect 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,361 @@
1
+ 'use strict';
2
+
3
+ const { test, describe } = require('node:test');
4
+ const assert = require('node:assert/strict');
5
+
6
+ const InspectLink = require('../src/InspectLink');
7
+ const ItemPreviewData = require('../src/ItemPreviewData');
8
+ const Sticker = require('../src/Sticker');
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Known test vectors
12
+ // ---------------------------------------------------------------------------
13
+
14
+ // A real CS2 item encoded with XOR key 0xE3
15
+ const NATIVE_HEX = (
16
+ 'E3F3367440334DE2FBE4C345E0CBE0D3E7DB6943400AE0A379E481ECEBE2F36F' +
17
+ 'D9DE2BDB515EA6E30D74D981ECEBE3F37BCBDE640D475DA6E35EFCD881ECEBE3' +
18
+ 'F359D5DE37E9D75DA6436DD3DD81ECEBE3F366DCDE3F8F9BDDA69B43B6DE81EC' +
19
+ 'EBE3F33BC8DEBB1CA3DFA623F7DDDF8B71E293EBFD43382B'
20
+ );
21
+
22
+ // A tool-generated link with key 0x00
23
+ const TOOL_HEX = '00183C20B803280538E9A3C5DD0340E102C246A0D1';
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Helper
27
+ // ---------------------------------------------------------------------------
28
+ function roundtrip(data) {
29
+ return InspectLink.deserialize(InspectLink.serialize(data));
30
+ }
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Deserialize tests — native XOR key 0xE3
34
+ // ---------------------------------------------------------------------------
35
+
36
+ describe('deserialize — native XOR link (key 0xE3)', () => {
37
+ test('itemid', () => {
38
+ const item = InspectLink.deserialize(NATIVE_HEX);
39
+ assert.equal(item.itemId, 46876117973);
40
+ });
41
+
42
+ test('defindex (AK-47)', () => {
43
+ const item = InspectLink.deserialize(NATIVE_HEX);
44
+ assert.equal(item.defIndex, 7);
45
+ });
46
+
47
+ test('paintindex', () => {
48
+ const item = InspectLink.deserialize(NATIVE_HEX);
49
+ assert.equal(item.paintIndex, 422);
50
+ });
51
+
52
+ test('paintseed', () => {
53
+ const item = InspectLink.deserialize(NATIVE_HEX);
54
+ assert.equal(item.paintSeed, 922);
55
+ });
56
+
57
+ test('paintwear approximately 0.04121', () => {
58
+ const item = InspectLink.deserialize(NATIVE_HEX);
59
+ assert.ok(Math.abs(item.paintWear - 0.04121) < 0.0001, `Expected ~0.04121, got ${item.paintWear}`);
60
+ });
61
+
62
+ test('rarity', () => {
63
+ const item = InspectLink.deserialize(NATIVE_HEX);
64
+ assert.equal(item.rarity, 3);
65
+ });
66
+
67
+ test('quality', () => {
68
+ const item = InspectLink.deserialize(NATIVE_HEX);
69
+ assert.equal(item.quality, 4);
70
+ });
71
+
72
+ test('sticker count = 5', () => {
73
+ const item = InspectLink.deserialize(NATIVE_HEX);
74
+ assert.equal(item.stickers.length, 5);
75
+ });
76
+
77
+ test('sticker IDs [7436, 5144, 6970, 8069, 5592]', () => {
78
+ const item = InspectLink.deserialize(NATIVE_HEX);
79
+ const ids = item.stickers.map(s => s.stickerId);
80
+ assert.deepEqual(ids, [7436, 5144, 6970, 8069, 5592]);
81
+ });
82
+ });
83
+
84
+ // ---------------------------------------------------------------------------
85
+ // Deserialize tests — tool hex (key 0x00)
86
+ // ---------------------------------------------------------------------------
87
+
88
+ describe('deserialize — tool-generated link (key 0x00)', () => {
89
+ test('defindex', () => {
90
+ const item = InspectLink.deserialize(TOOL_HEX);
91
+ assert.equal(item.defIndex, 60);
92
+ });
93
+
94
+ test('paintindex', () => {
95
+ const item = InspectLink.deserialize(TOOL_HEX);
96
+ assert.equal(item.paintIndex, 440);
97
+ });
98
+
99
+ test('paintseed', () => {
100
+ const item = InspectLink.deserialize(TOOL_HEX);
101
+ assert.equal(item.paintSeed, 353);
102
+ });
103
+
104
+ test('paintwear', () => {
105
+ const item = InspectLink.deserialize(TOOL_HEX);
106
+ assert.ok(
107
+ Math.abs(item.paintWear - 0.005411375779658556) < 1e-7,
108
+ `Expected ~0.005411375779658556, got ${item.paintWear}`,
109
+ );
110
+ });
111
+
112
+ test('rarity', () => {
113
+ const item = InspectLink.deserialize(TOOL_HEX);
114
+ assert.equal(item.rarity, 5);
115
+ });
116
+
117
+ test('lowercase hex accepted', () => {
118
+ const item = InspectLink.deserialize(TOOL_HEX.toLowerCase());
119
+ assert.equal(item.defIndex, 60);
120
+ });
121
+
122
+ test('full steam:// URL accepted', () => {
123
+ const url = `steam://rungame/730/76561202255233023/+csgo_econ_action_preview%20A${TOOL_HEX}`;
124
+ const item = InspectLink.deserialize(url);
125
+ assert.equal(item.defIndex, 60);
126
+ });
127
+
128
+ test('csgo:// style URL with literal space accepted', () => {
129
+ const url = `csgo://rungame/730/76561202255233023/+csgo_econ_action_preview A${TOOL_HEX}`;
130
+ const item = InspectLink.deserialize(url);
131
+ assert.equal(item.defIndex, 60);
132
+ });
133
+
134
+ test('payload too short throws TypeError', () => {
135
+ assert.throws(() => InspectLink.deserialize('0000'), TypeError);
136
+ });
137
+ });
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // Serialize tests
141
+ // ---------------------------------------------------------------------------
142
+
143
+ describe('serialize', () => {
144
+ test('known hex output matches TOOL_HEX', () => {
145
+ const data = new ItemPreviewData({
146
+ defIndex: 60,
147
+ paintIndex: 440,
148
+ paintSeed: 353,
149
+ paintWear: 0.005411375779658556,
150
+ rarity: 5,
151
+ });
152
+ assert.equal(InspectLink.serialize(data), TOOL_HEX);
153
+ });
154
+
155
+ test('returns uppercase hex', () => {
156
+ const data = new ItemPreviewData({ defIndex: 1 });
157
+ const result = InspectLink.serialize(data);
158
+ assert.equal(result, result.toUpperCase());
159
+ });
160
+
161
+ test('starts with "00" (key_byte = 0x00)', () => {
162
+ const data = new ItemPreviewData({ defIndex: 1 });
163
+ assert.ok(InspectLink.serialize(data).startsWith('00'));
164
+ });
165
+
166
+ test('minimum length >= 12 hex chars (6 bytes)', () => {
167
+ const data = new ItemPreviewData({ defIndex: 1 });
168
+ assert.ok(InspectLink.serialize(data).length >= 12);
169
+ });
170
+ });
171
+
172
+ // ---------------------------------------------------------------------------
173
+ // Round-trip tests
174
+ // ---------------------------------------------------------------------------
175
+
176
+ describe('round-trip', () => {
177
+ test('defindex', () => {
178
+ assert.equal(roundtrip(new ItemPreviewData({ defIndex: 7 })).defIndex, 7);
179
+ });
180
+
181
+ test('paintindex', () => {
182
+ assert.equal(roundtrip(new ItemPreviewData({ paintIndex: 422 })).paintIndex, 422);
183
+ });
184
+
185
+ test('paintseed', () => {
186
+ assert.equal(roundtrip(new ItemPreviewData({ paintSeed: 999 })).paintSeed, 999);
187
+ });
188
+
189
+ test('paintwear float32 precision', () => {
190
+ const original = 0.123456789;
191
+ // Compute expected float32 round-trip value
192
+ const dv = new DataView(new ArrayBuffer(4));
193
+ dv.setFloat32(0, original, true);
194
+ const expected = dv.getFloat32(0, true);
195
+ const result = roundtrip(new ItemPreviewData({ paintWear: original }));
196
+ assert.ok(Math.abs(result.paintWear - expected) < 1e-7);
197
+ });
198
+
199
+ test('large itemid (46876117973)', () => {
200
+ const result = roundtrip(new ItemPreviewData({ itemId: 46876117973 }));
201
+ assert.equal(result.itemId, 46876117973);
202
+ });
203
+
204
+ test('stickers — count and ids', () => {
205
+ const data = new ItemPreviewData({
206
+ defIndex: 7,
207
+ stickers: [
208
+ new Sticker({ slot: 0, stickerId: 7436 }),
209
+ new Sticker({ slot: 1, stickerId: 5144 }),
210
+ ],
211
+ });
212
+ const result = roundtrip(data);
213
+ assert.equal(result.stickers.length, 2);
214
+ assert.equal(result.stickers[0].stickerId, 7436);
215
+ assert.equal(result.stickers[1].stickerId, 5144);
216
+ });
217
+
218
+ test('sticker slot', () => {
219
+ const data = new ItemPreviewData({
220
+ stickers: [new Sticker({ slot: 3, stickerId: 123 })],
221
+ });
222
+ assert.equal(roundtrip(data).stickers[0].slot, 3);
223
+ });
224
+
225
+ test('sticker wear (fixed32 float)', () => {
226
+ const data = new ItemPreviewData({
227
+ stickers: [new Sticker({ stickerId: 1, wear: 0.5 })],
228
+ });
229
+ const result = roundtrip(data);
230
+ assert.ok(result.stickers[0].wear !== null);
231
+ assert.ok(Math.abs(result.stickers[0].wear - 0.5) < 1e-6);
232
+ });
233
+
234
+ test('keychains — stickerId and pattern', () => {
235
+ const data = new ItemPreviewData({
236
+ keychains: [new Sticker({ slot: 0, stickerId: 999, pattern: 42 })],
237
+ });
238
+ const result = roundtrip(data);
239
+ assert.equal(result.keychains.length, 1);
240
+ assert.equal(result.keychains[0].stickerId, 999);
241
+ assert.equal(result.keychains[0].pattern, 42);
242
+ });
243
+
244
+ test('customName string', () => {
245
+ const data = new ItemPreviewData({ defIndex: 7, customName: 'My Knife' });
246
+ assert.equal(roundtrip(data).customName, 'My Knife');
247
+ });
248
+
249
+ test('rarity and quality', () => {
250
+ const data = new ItemPreviewData({ rarity: 6, quality: 9 });
251
+ const result = roundtrip(data);
252
+ assert.equal(result.rarity, 6);
253
+ assert.equal(result.quality, 9);
254
+ });
255
+
256
+ test('full item with 5 stickers', () => {
257
+ const data = new ItemPreviewData({
258
+ itemId: 46876117973,
259
+ defIndex: 7,
260
+ paintIndex: 422,
261
+ rarity: 3,
262
+ quality: 4,
263
+ paintWear: 0.04121,
264
+ paintSeed: 922,
265
+ stickers: [
266
+ new Sticker({ slot: 0, stickerId: 7436 }),
267
+ new Sticker({ slot: 1, stickerId: 5144 }),
268
+ new Sticker({ slot: 2, stickerId: 6970 }),
269
+ new Sticker({ slot: 3, stickerId: 8069 }),
270
+ new Sticker({ slot: 4, stickerId: 5592 }),
271
+ ],
272
+ });
273
+ const result = roundtrip(data);
274
+ assert.equal(result.defIndex, 7);
275
+ assert.equal(result.paintIndex, 422);
276
+ assert.equal(result.paintSeed, 922);
277
+ assert.equal(result.stickers.length, 5);
278
+ assert.deepEqual(result.stickers.map(s => s.stickerId), [7436, 5144, 6970, 8069, 5592]);
279
+ });
280
+
281
+ test('empty stickers array', () => {
282
+ const data = new ItemPreviewData({ defIndex: 7, stickers: [] });
283
+ const result = roundtrip(data);
284
+ assert.equal(result.stickers.length, 0);
285
+ });
286
+ });
287
+
288
+ // ---------------------------------------------------------------------------
289
+ // Validation and hybrid URL tests
290
+ // ---------------------------------------------------------------------------
291
+
292
+ const HYBRID_URL = (
293
+ 'steam://rungame/730/76561202255233023/+csgo_econ_action_preview%20' +
294
+ 'S76561199323320483A50075495125D1101C4C4FCD4AB10092D31B8143914211829A1FAE3FD125119591141117308191301' +
295
+ 'EA550C1111912E3C111151D12C413E6BAC54D1D29BAD731E191501B92C2C9B6BF92F5411C25B2A731E191501B92C2C' +
296
+ 'EA2B182E5411F7212A731E191501B92C2C4F89C12F549164592A799713611956F4339F'
297
+ );
298
+
299
+ const CLASSIC_URL = (
300
+ 'steam://rungame/730/76561202255233023/+csgo_econ_action_preview%20' +
301
+ 'S76561199842063946A49749521570D2751293026650298712'
302
+ );
303
+
304
+ describe('isMasked', () => {
305
+ test('returns true for pure hex payload URL', () => {
306
+ const url = `steam://run/730//+csgo_econ_action_preview%20${TOOL_HEX}`;
307
+ assert.equal(InspectLink.isMasked(url), true);
308
+ });
309
+
310
+ test('returns true for full native masked URL', () => {
311
+ const url = `steam://rungame/730/76561202255233023/+csgo_econ_action_preview%20${NATIVE_HEX}`;
312
+ assert.equal(InspectLink.isMasked(url), true);
313
+ });
314
+
315
+ test('returns true for hybrid URL', () => {
316
+ assert.equal(InspectLink.isMasked(HYBRID_URL), true);
317
+ });
318
+
319
+ test('returns false for classic URL', () => {
320
+ assert.equal(InspectLink.isMasked(CLASSIC_URL), false);
321
+ });
322
+ });
323
+
324
+ describe('isClassic', () => {
325
+ test('returns true for classic URL', () => {
326
+ assert.equal(InspectLink.isClassic(CLASSIC_URL), true);
327
+ });
328
+
329
+ test('returns false for masked URL', () => {
330
+ const url = `steam://run/730//+csgo_econ_action_preview%20${TOOL_HEX}`;
331
+ assert.equal(InspectLink.isClassic(url), false);
332
+ });
333
+
334
+ test('returns false for hybrid URL', () => {
335
+ assert.equal(InspectLink.isClassic(HYBRID_URL), false);
336
+ });
337
+ });
338
+
339
+ describe('deserialize — hybrid URL', () => {
340
+ test('itemId equals assetId from URL (50075495125)', () => {
341
+ const item = InspectLink.deserialize(HYBRID_URL);
342
+ assert.equal(item.itemId, 50075495125);
343
+ });
344
+ });
345
+
346
+ // ---------------------------------------------------------------------------
347
+ // Checksum test
348
+ // ---------------------------------------------------------------------------
349
+
350
+ describe('checksum', () => {
351
+ test('known hex checksum matches', () => {
352
+ const data = new ItemPreviewData({
353
+ defIndex: 60,
354
+ paintIndex: 440,
355
+ paintSeed: 353,
356
+ paintWear: 0.005411375779658556,
357
+ rarity: 5,
358
+ });
359
+ assert.equal(InspectLink.serialize(data), TOOL_HEX);
360
+ });
361
+ });