payload.spec.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { mkdtemp, rm, writeFile } from 'node:fs/promises'
  2. import { tmpdir } from 'node:os'
  3. import { join } from 'node:path'
  4. import { afterEach, describe, expect, it } from 'vitest'
  5. import { SecretRedactor, buildTelemetryPayload } from '@deepseek-ai/dsh-telemetry'
  6. const dirs: string[] = []
  7. async function projectDir(files: Record<string, string>): Promise<string> {
  8. const dir = await mkdtemp(join(tmpdir(), 'dsh-payload-'))
  9. dirs.push(dir)
  10. await Promise.all(Object.entries(files).map(([name, content]) => writeFile(join(dir, name), content, 'utf8')))
  11. return dir
  12. }
  13. afterEach(async () => {
  14. await Promise.all(dirs.splice(0).map(dir => rm(dir, { recursive: true, force: true })))
  15. })
  16. describe('buildTelemetryPayload', () => {
  17. it('carries lifecycle facts and redacted file content', async () => {
  18. const dir = await projectDir({
  19. 'cordis.yml': '- id: llm\n name: \'@deepseek-ai/dsh-llm-deepseek\'\n config:\n apiKey: sk-abcdefghij1234567890\n',
  20. 'package.json': '{ "name": "my-app", "config": { "token": "sk-abcdefghij1234567890" } }',
  21. })
  22. const payload = await buildTelemetryPayload({ command: 'build', durationMs: 42, success: true, projectDir: dir })
  23. expect(payload.command).toBe('build')
  24. expect(payload.durationMs).toBe(42)
  25. expect(payload.success).toBe(true)
  26. expect(payload.cordisYmlContent).toContain('@deepseek-ai/dsh-llm-deepseek') // package name preserved
  27. expect(payload.cordisYmlContent).not.toContain('sk-abcdefghij1234567890') // secret scrubbed
  28. expect(payload.packageJsonContent).toContain('my-app')
  29. expect(payload.packageJsonContent).not.toContain('sk-abcdefghij1234567890')
  30. })
  31. it('omits fields whose files do not exist', async () => {
  32. const dir = await projectDir({ 'cordis.yml': '- id: llm\n name: \'@deepseek-ai/dsh-llm-deepseek\'\n' })
  33. const payload = await buildTelemetryPayload({ command: 'create', durationMs: 1, success: false, projectDir: dir })
  34. expect(payload.cordisYmlContent).toBeDefined()
  35. expect('packageJsonContent' in payload).toBe(false)
  36. })
  37. it('omits both fields when neither file exists', async () => {
  38. const dir = await projectDir({})
  39. const payload = await buildTelemetryPayload({ command: 'create', durationMs: 0, success: true, projectDir: dir })
  40. expect('cordisYmlContent' in payload).toBe(false)
  41. expect('packageJsonContent' in payload).toBe(false)
  42. })
  43. it('withholds package.json when cordis.yml is absent (not an SDK project)', async () => {
  44. const dir = await projectDir({ 'package.json': '{ "name": "unrelated-repo" }' })
  45. const payload = await buildTelemetryPayload({ command: 'build', durationMs: 3, success: false, projectDir: dir })
  46. expect('cordisYmlContent' in payload).toBe(false)
  47. expect('packageJsonContent' in payload).toBe(false)
  48. })
  49. it('uses a supplied redactor', async () => {
  50. const dir = await projectDir({
  51. 'cordis.yml': '- id: llm\n name: \'@deepseek-ai/dsh-llm-deepseek\'\n',
  52. 'package.json': '{ "password": "hunter2" }',
  53. })
  54. const redactor = new SecretRedactor({ placeholder: '<<hidden>>' })
  55. const payload = await buildTelemetryPayload({
  56. command: 'config', durationMs: 5, success: true, projectDir: dir, redactor,
  57. })
  58. expect(payload.packageJsonContent).toContain('<<hidden>>')
  59. expect(payload.packageJsonContent).not.toContain('hunter2')
  60. })
  61. })