• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

suculent / thinx-device-api / #252646138

07 May 2025 07:51PM UTC coverage: 72.01% (+25.6%) from 46.379%
#252646138

push

suculent
base update (weird issue with couchdb Dockerfile where is no change)

1830 of 3464 branches covered (52.83%)

Branch coverage included in aggregate %.

8183 of 10441 relevant lines covered (78.37%)

7.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.0
/spec/jasmine/ZZ-RouterBuilderSpec.js
1
/* Router integration test only; does not have to cover full unit functionality. */
2

3
const THiNX = require("../../thinx-core.js");
1✔
4

5
let chai = require('chai');
1✔
6
var expect = require('chai').expect;
1✔
7
let chaiHttp = require('chai-http');
1✔
8
chai.use(chaiHttp);
1✔
9

10
var envi = require("../_envi.json");
1✔
11

12
const dynamic_owner_id = envi.dynamic.owner;
1✔
13

14

15
//
16
// Unauthenticated
17
//
18

19
let thx;
20

21
describe("Builder (noauth)", function () {
1✔
22

23
    beforeAll((done) => {
1✔
24
        console.log(`🚸 [chai] >>> running Builder (noauth) spec`);
1✔
25
        thx = new THiNX();
1✔
26
        thx.on('workerReady', () => {
1✔
27
            console.log("🚸🚸🚸 [spec] [emit] worker ready! 🚸🚸🚸"); // should allow waiting for worker beforeAll
×
28
            done();
×
29
        });
30
        thx.init(() => {
1✔
31
            done();
1✔
32
        });
33
    }, 30000);
34

35
    // run build manually
36
    it("POST /api/build", function (done) {
1✔
37
        chai.request(thx.app)
1✔
38
            .post('/api/build')
39
            .send({})
40
            .end((err, res) => {
41
                console.log("🚸 [chai] response /api/build:", res.text, " status:", res.status);
1✔
42
                expect(res.status).to.equal(401);
1✔
43
                //expect(res.text).to.be.a('string');
44
                done();
1✔
45
            });
46
    }, 30000);
47

48
    // latest firmware envelope
49
    it("POST /api/device/envelope", function (done) {
1✔
50
        chai.request(thx.app)
1✔
51
            .post('/api/device/envelope')
52
            .send({})
53
            .end((err, res) => {
54
                console.log("🚸 [chai] response /api/device/envelope:", res.text, " status:", res.status);
1✔
55
                expect(res.status).to.equal(200);
1✔
56
                expect(res.text).to.be.a('string'); // false
1✔
57
                done();
1✔
58
            });
59
    }, 30000);
60

61
    // get build artifacts
62
    it("POST /api/device/artifacts", function (done) {
1✔
63
        chai.request(thx.app)
1✔
64
            .post('/api/device/artifacts')
65
            .send({})
66
            .end((err, res) => {
67
                console.log("🚸 [chai] response /api/device/artifacts:", res.text, " status:", res.status);
1✔
68
                expect(res.status).to.equal(401);
1✔
69
                done();
1✔
70
            });
71
    }, 30000);
72

73
    afterAll(() => {
1✔
74
        console.log(`🚸 [chai] <<< completed Builder (noauth) spec`);
1✔
75
      });
76

77
});
78

79
//
80
// Authenticated
81
//
82

83
describe("Builder (JWT)", function () {
1✔
84

85
    let agent;
86
    let jwt;
87

88
    beforeAll((done) => {
1✔
89
        agent = chai.request.agent(thx.app);
1✔
90
        console.log(`🚸 [chai] Builder (JWT) spec`);
1✔
91
        agent
1✔
92
            .post('/api/login')
93
            .send({ username: 'dynamic', password: 'dynamic', remember: false })
94
            .then(function (res) {
95
                console.log(`[chai] beforeAll POST /api/login (valid) response: ${res}`);
1✔
96
                expect(res).to.have.cookie('x-thx-core');
1✔
97
                let body = JSON.parse(res.text);
1✔
98
                jwt = 'Bearer ' + body.access_token;
1✔
99
                done();
1✔
100
            })
101
            .catch((e) => { console.log(e); });
×
102
    });
103

104
    afterAll((done) => {
1✔
105
        agent.close();
1✔
106
        console.log(`🚸 [chai] <<< completed Builder (JWT) spec`);
1✔
107
        done();
1✔
108
    });
109

110
    // run build manually
111
    it("POST /api/build (JWT, invalid) I", function (done) {
1✔
112
        agent
1✔
113
            .post('/api/build')
114
            .set('Authorization', jwt)
115
            .send({})
116
            .end((err, res) => {
117
                expect(res.status).to.equal(400);
1✔
118
                done();
1✔
119
            });
120
    }, 30000);
121
    
122
    // covering buildGuards
123

124
    it("POST /api/build (JWT, invalid) II", function (done) {
1✔
125
        agent
1✔
126
            .post('/api/build')
127
            .set('Authorization', jwt)
128
            .send({ owner: dynamic_owner_id, git: "something", branch: "origin/master" })
129
            .end((err, res) => {
130
                expect(res.status).to.equal(400);
1✔
131
                done();
1✔
132
            });
133
    }, 30000);
134

135
    it("POST /api/build (JWT, invalid) III", function (done) {
1✔
136
        agent
1✔
137
            .post('/api/build')
138
            .set('Authorization', jwt)
139
            .send({ git: "something", branch: "origin/master" })
140
            .end((err, res) => {
141
                expect(res.status).to.equal(400);
1✔
142
                done();
1✔
143
            });
144
    }, 30000);
145

146
    it("POST /api/build (JWT, invalid) IV", function (done) {
1✔
147
        agent
1✔
148
            .post('/api/build')
149
            .set('Authorization', jwt)
150
            .send({ owner: dynamic_owner_id, branch: "origin/master" })
151
            .end((err, res) => {
152
                expect(res.status).to.equal(400);
1✔
153
                done();
1✔
154
            });
155
    }, 30000);
156

157
    it("POST /api/build (JWT, invalid) V", function (done) {
1✔
158
        agent
1✔
159
            .post('/api/build')
160
            .set('Authorization', jwt)
161
            .send({ owner: dynamic_owner_id, git: "origin/master" })
162
            .end((err, res) => {
163
                expect(res.status).to.equal(400);
1✔
164
                done();
1✔
165
            });
166
    }, 30000);
167

168
    it("POST /api/build (JWT, invalid) VI", function (done) {
1✔
169
        agent
1✔
170
            .post('/api/build')
171
            .set('Authorization', jwt)
172
            .send({ owner: dynamic_owner_id, git: "origin/master", udid: null })
173
            .end((err, res) => {
174
                expect(res.status).to.equal(400);
1✔
175
                done();
1✔
176
            });
177
    }, 30000);
178

179
    it("POST /api/build (JWT, invalid) VII", function (done) {
1✔
180
        agent
1✔
181
            .post('/api/build')
182
            .set('Authorization', jwt)
183
            .send({ owner: dynamic_owner_id, git: "origin/master", source_id: null })
184
            .end((err, res) => {
185
                expect(res.status).to.equal(400);
1✔
186
                done();
1✔
187
            });
188
    }, 30000);
189

190
    it("POST /api/build (JWT, valid) VI", function (done) {
1✔
191

192
        agent
1✔
193
            .post('/api/device/attach')
194
            .set('Authorization', jwt)
195
            .send({ 
196
                udid: envi.dynamic.udid, 
197
                source_id: "7038e0500a8690a8bf70d8470f46365458798011e8f46ff012f12cbcf898b2f4" 
198
            })
199
            .end((err, res) => {
200
                console.log("🚸 [chai] POST /api/device/attach response:", res.text, " status:", res.status);
1✔
201
                //expect(res.status).to.equal(200);
202
                //expect(res.text).to.be.a('string');
203

204
                agent
1✔
205
                    .post('/api/build')
206
                    .set('Authorization', jwt)
207
                    .send({
208
                        owner: dynamic_owner_id,
209
                        git: "https://github.com/suculent/thinx-firmware-esp8266-pio",
210
                        branch: "origin/master",
211
                        udid: envi.dynamic.udid,
212
                        source_id: "7038e0500a8690a8bf70d8470f46365458798011e8f46ff012f12cbcf898b2f4",
213
                        build_id: envi.dynamic.udid
214
                    })
215
                    .end((err, res) => {
216
                        console.log("🚸 [chai] response /api/build (JWT, invalid) V:", res.text, " status:", res.status);
1✔
217
                        expect(res.status).to.equal(400);
1✔
218
                        //expect(res.text).to.be.a('string');
219
                        done();
1✔
220
                    });
221
            });
222

223
        
224
    }, 30000);
225

226
    it("POST /api/v2/build (JWT, valid) V", function (done) {
1✔
227

228
        agent
1✔
229
            .post('/api/device/attach')
230
            .set('Authorization', jwt)
231
            .send({ 
232
                udid: envi.dynamic.udid, 
233
                source_id: "7038e0500a8690a8bf70d8470f46365458798011e8f46ff012f12cbcf898b2f4" 
234
            })
235
            .end((_err, res) => {
236
                expect(res.status).to.equal(200);
1✔
237
                expect(res.text).to.be.a('string');
1✔
238

239
                agent
1✔
240
                    .post('/api/v2/build')
241
                    .set('Authorization', jwt)
242
                    .send({ build: {
243
                            owner: dynamic_owner_id,
244
                            git: "https://github.com/suculent/thinx-firmware-esp8266-pio",
245
                            branch: "origin/master",
246
                            udid: envi.dynamic.udid,
247
                            source_id: "7038e0500a8690a8bf70d8470f46365458798011e8f46ff012f12cbcf898b2f4",
248
                            build: envi.dynamic.udid
249
                        }
250
                    })
251
                    .end((err, res) => {
252
                        expect(res.status).to.equal(200);
1✔
253
                        expect(res.text).to.equal('{"success":true,"response":"queued"}');
1✔
254
                        done();
1✔
255
                    });
256
            });
257

258
        
259
    }, 30000);
260

261
    // latest firmware envelope
262
    it("POST /api/device/envelope (JWT, invalid)", function (done) {
1✔
263
        agent
1✔
264
            .post('/api/device/envelope')
265
            .set('Authorization', jwt)
266
            .send({})
267
            .end((err, res) => {
268
                expect(res.status).to.equal(200);
1✔
269
                expect(res.text).to.be.a('string');
1✔
270
                expect(res.text).to.equal('{"success":false,"response":{}}');
1✔
271
                done();
1✔
272
            });
273
    }, 30000);
274

275
    it("POST /api/v2/device/lastbuild (JWT, invalid)", function (done) {
1✔
276
        agent
1✔
277
            .post('/api/v2/device/lastbuild')
278
            .set('Authorization', jwt)
279
            .send({})
280
            .end((err, res) => {
281
                expect(res.status).to.equal(200);
1✔
282
                expect(res.text).to.be.a('string');
1✔
283
                expect(res.text).to.equal('{"success":false,"response":{}}');
1✔
284
                done();
1✔
285
            });
286
    }, 30000);
287

288
    // get build artifacts
289
    it("POST /api/device/artifacts (JWT, invalid)", function (done) {
1✔
290
        agent
1✔
291
            .post('/api/device/artifacts')
292
            .set('Authorization', jwt)
293
            .send({})
294
            .end((err, res) => {
295
                expect(res.status).to.equal(400);
1✔
296
                expect(res.text).to.be.a('string');
1✔
297
                expect(res.text).to.equal('{"success":false,"response":"missing_owner"}');
1✔
298
                done();
1✔
299
            });
300
    }, 30000);
301

302
    it("POST /api/device/artifacts (JWT, semi-valid 1)", function (done) {
1✔
303
        agent
1✔
304
            .post('/api/device/artifacts')
305
            .set('Authorization', jwt)
306
            .send({ udid: envi.dynamic.udid })
307
            .end((err, res) => {
308
                expect(res.status).to.equal(400);
1✔
309
                expect(res.text).to.be.a('string');
1✔
310
                expect(res.text).to.equal('{"success":false,"response":"missing_owner"}');
1✔
311
                done();
1✔
312
            });
313
    }, 30000);
314

315
    it("POST /api/device/artifacts (JWT, semi-valid 2)", function (done) {
1✔
316
        agent
1✔
317
            .post('/api/device/artifacts')
318
            .set('Authorization', jwt)
319
            .send({ build_id: envi.dynamic.udid })
320
            .end((err, res) => {
321
                expect(res.status).to.equal(400);
1✔
322
                expect(res.text).to.be.a('string');
1✔
323
                expect(res.text).to.equal('{"success":false,"response":"missing_owner"}');
1✔
324
                done();
1✔
325
            });
326
    }, 30000);
327

328
    it("POST /api/device/artifacts (JWT, semi-valid 3)", function (done) {
1✔
329
        agent
1✔
330
            .post('/api/device/artifacts')
331
            .set('Authorization', jwt)
332
            .send({ udid: envi.dynamic.udid, build_id: envi.dynamic.udid  })
333
            .end((err, res) => {
334
                expect(res.status).to.equal(400);
1✔
335
                expect(res.text).to.be.a('string');
1✔
336
                expect(res.text).to.equal('{"success":false,"response":"missing_owner"}');
1✔
337
                done();
1✔
338
            });
339
    }, 30000);
340

341
    it("POST /api/v2/build/artifacts (JWT, semi-valid 4)", function (done) {
1✔
342
        agent
1✔
343
            .post('/api/v2/build/artifacts')
344
            .set('Authorization', jwt)
345
            .send({ udid: envi.dynamic.udid, build_id: envi.dynamic.udid, owner: envi.dynamic.owner  })
346
            .end((err, res) => {
347
                expect(res.status).to.equal(200); // returns 200 (semi-valid) - response cannot be changed here unless res passed to build(!)
1✔
348
                expect(res.text).to.be.a('string');
1✔
349
                expect(res.text).to.equal('{"success":false,"response":"artifact_not_found"}');
1✔
350
                done();
1✔
351
            });
352
    }, 30000);
353

354
    it("POST /api/v2/build/artifacts (JWT, still-invalid)", function (done) {
1✔
355
        agent
1✔
356
            .post('/api/v2/build/artifacts')
357
            .set('Authorization', jwt)
358
            .send({ udid: envi.dynamic.udid, build_id: envi.dynamic.owner, owner: envi.dynamic.owner  })
359
            .end((err, res) => {
360
                expect(res.status).to.equal(400);
1✔
361
                done();
1✔
362
            });
363
    }, 30000);
364

365
    // the artifact is mocked at mnt/data/deploy/<owner-id>/<udid>/<build_id>/<build_id>.zip
366
    it("POST /api/v2/build/artifacts (JWT, no-udid)", function (done) {
1✔
367
        agent
1✔
368
            .post('/api/v2/build/artifacts')
369
            .set('Authorization', jwt)
370
            .send({ build_id: envi.build_id, owner: envi.dynamic.owner  })
371
            .end((err, res) => {
372
                expect(res.status).to.equal(400);
1✔
373
                done();
1✔
374
            });
375
    }, 30000);
376

377
    it("POST /api/v2/build/artifacts (JWT, no-owner)", function (done) {
1✔
378
        agent
1✔
379
            .post('/api/v2/build/artifacts')
380
            .set('Authorization', jwt)
381
            .send({ udid: envi.dynamic.udid, build_id: envi.build_id  })
382
            .end((err, res) => {
383
                expect(res.status).to.equal(400);
1✔
384
                done();
1✔
385
            });
386
    }, 30000);
387

388
    // the artifact is mocked at mnt/data/deploy/<owner-id>/<udid>/<build_id>/<build_id>.zip
389
    it("POST /api/v2/build/artifacts (JWT, valid)", function (done) {
1✔
390
        agent
1✔
391
            .post('/api/v2/build/artifacts')
392
            .set('Authorization', jwt)
393
            .send({ udid: envi.dynamic.udid, build_id: envi.build_id, owner: envi.dynamic.owner  })
394
            .end((err, res) => {
395
                expect(res.status).to.equal(200);
1✔
396
                done();
1✔
397
            });
398
    }, 30000);
399

400
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc