surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
| @@ -8,29 +8,36 @@ | ||
| 8 | 8 | import type { Plugin } from 'claude-code/testing' |
| 9 | 9 | import { expect, memoryEnv, seat, test } from 'claude-code/testing' |
| 10 | 10 | |
| 11 | +import type { LogEntry } from '../hooks/telemetry-types' | |
| 12 | + | |
| 11 | 13 | seat('builtin') |
| 12 | 14 | |
| 13 | 15 | const BEARER: SessionAuthorization = { handle: 'the-handle', kind: 'bearer' } |
| 14 | 16 | const ACCEPTED: HttpResponse = { status: 200, ok: true, headers: {}, text: '' } |
| 15 | 17 | |
| 18 | +const SURVEY: LogEntry = { | |
| 19 | + event: 'survey_answered', | |
| 20 | + props: { answer: 2, seen: true }, | |
| 21 | +} | |
| 22 | + | |
| 16 | 23 | /** |
| 17 | - * `/record <event>`, which the recording plugin answers. | |
| 24 | + * `/record <entry>`, which the recording plugin answers. | |
| 18 | 25 | */ |
| 19 | -const record = (event: string): CommandRunInput => ({ | |
| 26 | +const record = (entry: LogEntry): CommandRunInput => ({ | |
| 20 | 27 | command: 'record', |
| 21 | - args: event, | |
| 28 | + args: JSON.stringify(entry), | |
| 22 | 29 | origin: { kind: 'composer' }, |
| 23 | 30 | }) |
| 24 | 31 | |
| 25 | 32 | /** |
| 26 | - * A plugin whose `/record <event>` logs that event through `$.telemetry`, | |
| 33 | + * A plugin whose `/record <entry>` logs the entry through `$.telemetry`, | |
| 27 | 34 | * answering "sent", or why the row was refused. |
| 28 | 35 | */ |
| 29 | 36 | const recording: Plugin = { |
| 30 | 37 | name: 'recording', |
| 31 | 38 | register(on) { |
| 32 | 39 | on('command.run', { command: 'record' }, ($, e) => |
| 33 | - $.telemetry.log({ event: e.args, props: { answer: 2, seen: true } }).then( | |
| 40 | + $.telemetry.log(JSON.parse(e.args)).then( | |
| 34 | 41 | () => ({ text: 'sent' }), |
| 35 | 42 | (error: unknown) => ({ text: String(error) }), |
| 36 | 43 | ), |
| @@ -73,7 +80,7 @@ | ||
| 73 | 80 | memoryEnv(on, { USER_TYPE: 'ant' }) |
| 74 | 81 | const posts = firstPartySession(on) |
| 75 | 82 | |
| 76 | - expect(await $.command.run(record('survey_answered'))).toEqual({ | |
| 83 | + expect(await $.command.run(record(SURVEY))).toEqual({ | |
| 77 | 84 | text: 'sent', |
| 78 | 85 | }) |
| 79 | 86 | expect(posts).toHaveLength(1) |
| @@ -103,7 +110,9 @@ | ||
| 103 | 110 | memoryEnv(on, {}) |
| 104 | 111 | const posts = firstPartySession(on) |
| 105 | 112 | |
| 106 | - await $.command.run(record('tengu_repl_diff_panel_shown')) | |
| 113 | + await $.command.run( | |
| 114 | + record({ ...SURVEY, event: 'tengu_repl_diff_panel_shown' }), | |
| 115 | + ) | |
| 107 | 116 | |
| 108 | 117 | expect(posts.map(batchOf)).toMatchObject([ |
| 109 | 118 | { |
| @@ -127,7 +136,7 @@ | ||
| 127 | 136 | memoryEnv(on, { DO_NOT_TRACK: '1' }) |
| 128 | 137 | const posts = firstPartySession(on) |
| 129 | 138 | |
| 130 | - expect(await $.command.run(record('survey_answered'))).toEqual({ | |
| 139 | + expect(await $.command.run(record(SURVEY))).toEqual({ | |
| 131 | 140 | text: 'sent', |
| 132 | 141 | }) |
| 133 | 142 | expect(posts).toEqual([]) |
| @@ -141,7 +150,7 @@ | ||
| 141 | 150 | memoryEnv(on, { CLAUDE_CODE_USE_BEDROCK: '1' }) |
| 142 | 151 | const posts = firstPartySession(on) |
| 143 | 152 | |
| 144 | - await $.command.run(record('survey_answered')) | |
| 153 | + await $.command.run(record(SURVEY)) | |
| 145 | 154 | |
| 146 | 155 | expect(posts).toEqual([]) |
| 147 | 156 | }, |
| @@ -153,7 +162,7 @@ | ||
| 153 | 162 | async ($, on) => { |
| 154 | 163 | memoryEnv(on, {}) |
| 155 | 164 | const posts = firstPartySession(on, null) |
| 156 | - const { text } = await $.command.run(record('survey_answered')) | |
| 165 | + const { text } = await $.command.run(record(SURVEY)) | |
| 157 | 166 | |
| 158 | 167 | expect(text).toEndWith( |
| 159 | 168 | '$.telemetry.log: this session has no first-party credential to ' + |
| @@ -162,3 +171,19 @@ | ||
| 162 | 171 | expect(posts).toEqual([]) |
| 163 | 172 | }, |
| 164 | 173 | ) |
| 174 | + | |
| 175 | +test( | |
| 176 | + 'free text in a row is refused, nothing sent', | |
| 177 | + { plugins: [recording] }, | |
| 178 | + async ($, on) => { | |
| 179 | + memoryEnv(on, {}) | |
| 180 | + const posts = firstPartySession(on) | |
| 181 | + const { text } = await $.command.run({ | |
| 182 | + ...record(SURVEY), | |
| 183 | + args: '{"event":"survey_answered","props":{"note":"hello world"}}', | |
| 184 | + }) | |
| 185 | + | |
| 186 | + expect(text).toContain('props.note: free text is refused') | |
| 187 | + expect(posts).toEqual([]) | |
| 188 | + }, | |
| 189 | +) |