feat: include job config and run history in export/import backup
Export bumped to version 3, now includes jobs (with raw unmasked config) and job_runs arrays. Import restores them when present and restarts the scheduler. Payloads without a jobs key leave jobs untouched, keeping v1/v2 backups fully compatible. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -276,9 +276,9 @@ describe('GET /api/export', () => {
|
||||
expect(res.body.instances).toEqual([])
|
||||
})
|
||||
|
||||
it('returns version 2', async () => {
|
||||
it('returns version 3', async () => {
|
||||
const res = await request(app).get('/api/export')
|
||||
expect(res.body.version).toBe(2)
|
||||
expect(res.body.version).toBe(3)
|
||||
})
|
||||
|
||||
it('includes a history array', async () => {
|
||||
@@ -287,6 +287,21 @@ describe('GET /api/export', () => {
|
||||
expect(res.body.history).toBeInstanceOf(Array)
|
||||
expect(res.body.history.some(e => e.field === 'created')).toBe(true)
|
||||
})
|
||||
|
||||
it('includes jobs and job_runs arrays', async () => {
|
||||
createJob(testJob)
|
||||
const res = await request(app).get('/api/export')
|
||||
expect(res.body.jobs).toBeInstanceOf(Array)
|
||||
expect(res.body.jobs).toHaveLength(1)
|
||||
expect(res.body.jobs[0].key).toBe('tailscale_sync')
|
||||
expect(res.body.job_runs).toBeInstanceOf(Array)
|
||||
})
|
||||
|
||||
it('exports raw job config without masking', async () => {
|
||||
createJob(testJob)
|
||||
const res = await request(app).get('/api/export')
|
||||
expect(res.body.jobs[0].config).toContain('tskey-test')
|
||||
})
|
||||
})
|
||||
|
||||
// ── POST /api/import ──────────────────────────────────────────────────────────
|
||||
@@ -341,6 +356,28 @@ describe('POST /api/import', () => {
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.body.imported).toBe(1)
|
||||
})
|
||||
|
||||
it('imports jobs and job_runs and returns imported_jobs count', async () => {
|
||||
const exp = await request(app).get('/api/export')
|
||||
createJob(testJob)
|
||||
const fullExport = await request(app).get('/api/export')
|
||||
const res = await request(app).post('/api/import').send({
|
||||
instances: fullExport.body.instances,
|
||||
history: fullExport.body.history,
|
||||
jobs: fullExport.body.jobs,
|
||||
job_runs: fullExport.body.job_runs,
|
||||
})
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.body.imported_jobs).toBe(1)
|
||||
expect((await request(app).get('/api/jobs')).body).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('leaves jobs untouched when no jobs key in payload', async () => {
|
||||
createJob(testJob)
|
||||
await request(app).post('/api/import')
|
||||
.send({ instances: [{ ...base, vmid: 1, name: 'x' }] })
|
||||
expect((await request(app).get('/api/jobs')).body).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
// ── Static assets & SPA routing ───────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user