mobile-app.test.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. import fs from 'node:fs'
  2. import os from 'node:os'
  3. import path from 'node:path'
  4. import Fastify from 'fastify'
  5. import { afterAll, describe, expect, it } from 'vitest'
  6. import { registerMobileApp } from '../src/http/mobile-app'
  7. const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'mobile-app-'))
  8. let published = false
  9. const f = Fastify()
  10. registerMobileApp(f, () => (published ? dir : undefined))
  11. afterAll(() => fs.rmSync(dir, { recursive: true, force: true }))
  12. describe('Android App 下载页', () => {
  13. it('还没有发布时页面提示,安装包与 index.json 返回 404', async () => {
  14. const page = await f.inject({ method: 'GET', url: '/app' })
  15. expect(page.statusCode).toBe(200)
  16. expect(page.headers['content-type']).toContain('text/html')
  17. expect(page.body).toContain('还没有发布安装包')
  18. expect((await f.inject({ method: 'GET', url: '/app/index.json' })).statusCode).toBe(404)
  19. expect((await f.inject({ method: 'GET', url: '/app/agent-0.1.0.apk' })).statusCode).toBe(404)
  20. })
  21. it('发布后显示版本与下载链接,安装包按附件下载', async () => {
  22. fs.writeFileSync(path.join(dir, 'agent-0.2.0.apk'), 'apk-bytes')
  23. fs.writeFileSync(path.join(dir, 'index.json'), JSON.stringify({ version: '0.2.0', file: 'agent-0.2.0.apk', size: 9, sha256: 'x', publishedAt: '2026-10-27T08:00:00Z' }))
  24. published = true
  25. const page = await f.inject({ method: 'GET', url: '/app', headers: { host: 'agent.example.com' } })
  26. expect(page.body).toContain('href="/app/agent-0.2.0.apk"')
  27. expect(page.body).toContain('版本 0.2.0')
  28. expect(page.body).toContain('http://agent.example.com')
  29. expect(page.body).toContain('<svg')
  30. expect((await f.inject({ method: 'GET', url: '/app/index.json' })).json().version).toBe('0.2.0')
  31. const apk = await f.inject({ method: 'GET', url: '/app/agent-0.2.0.apk' })
  32. expect(apk.statusCode).toBe(200)
  33. expect(apk.headers['content-type']).toBe('application/vnd.android.package-archive')
  34. expect(apk.headers['content-disposition']).toContain('attachment')
  35. expect(apk.body).toBe('apk-bytes')
  36. })
  37. it('只提供 .apk 文件', async () => {
  38. fs.writeFileSync(path.join(dir, 'secret.txt'), 'x')
  39. expect((await f.inject({ method: 'GET', url: '/app/secret.txt' })).statusCode).toBe(404)
  40. expect((await f.inject({ method: 'GET', url: '/app/..%2Findex.json.apk' })).statusCode).toBe(404)
  41. })
  42. })