|
| 1 | +import{handleGetCloudWatchTrace,handleListCloudWatchTraces}from'../handlers/cloudwatch-traces.js'; |
| 2 | +importtype{RouteContext}from'../handlers/route-context.js'; |
| 3 | +importtype{IncomingMessage,ServerResponse}from'http'; |
| 4 | +import{beforeEach,describe,expect,it,vi}from'vitest'; |
| 5 | + |
| 6 | +functionmockRes(): ServerResponse&{_status: number;_headers: Record<string,string>;_body: string}{ |
| 7 | +constres={ |
| 8 | +_status: 0, |
| 9 | +_headers: {}asRecord<string,string>, |
| 10 | +_body: '', |
| 11 | +writeHead(status: number,headers?: Record<string,string>){ |
| 12 | +res._status=status; |
| 13 | +if(headers)Object.assign(res._headers,headers); |
| 14 | +returnres; |
| 15 | +}, |
| 16 | +setHeader(name: string,value: string){ |
| 17 | +res._headers[name]=value; |
| 18 | +}, |
| 19 | +end(body?: string){ |
| 20 | +if(body)res._body=body; |
| 21 | +}, |
| 22 | +}; |
| 23 | +returnresasunknownasServerResponse&{_status: number;_headers: Record<string,string>;_body: string}; |
| 24 | +} |
| 25 | + |
| 26 | +functionmockReq(url: string): IncomingMessage{ |
| 27 | +return{ url,headers: {host: 'localhost:8081'}}asunknownasIncomingMessage; |
| 28 | +} |
| 29 | + |
| 30 | +functionmockCtx(overrides: Partial<RouteContext['options']>={}): RouteContext{ |
| 31 | +return{ |
| 32 | +options: { |
| 33 | +mode: 'dev', |
| 34 | +agents: [], |
| 35 | +harnesses: [], |
| 36 | +uiPort: 8081, |
| 37 | + ...overrides, |
| 38 | +}, |
| 39 | +runningAgents: newMap(), |
| 40 | +startingAgents: newMap(), |
| 41 | +agentErrors: newMap(), |
| 42 | +setCorsHeaders: vi.fn(), |
| 43 | +readBody: vi.fn(), |
| 44 | +}asRouteContext; |
| 45 | +} |
| 46 | + |
| 47 | +describe('handleListCloudWatchTraces',()=>{ |
| 48 | +beforeEach(()=>{ |
| 49 | +vi.restoreAllMocks(); |
| 50 | +}); |
| 51 | + |
| 52 | +it('returns 404 when no handler configured',async()=>{ |
| 53 | +constctx=mockCtx(); |
| 54 | +constreq=mockReq('/api/cloudwatch-traces?agentName=my-agent'); |
| 55 | +constres=mockRes(); |
| 56 | + |
| 57 | +awaithandleListCloudWatchTraces(ctx,req,res); |
| 58 | + |
| 59 | +expect(res._status).toBe(404); |
| 60 | +constbody=JSON.parse(res._body); |
| 61 | +expect(body.success).toBe(false); |
| 62 | +expect(body.error).toContain('not available'); |
| 63 | +}); |
| 64 | + |
| 65 | +it('returns 400 when neither agentName nor harnessName provided',async()=>{ |
| 66 | +consthandler=vi.fn(); |
| 67 | +constctx=mockCtx({onListCloudWatchTraces: handler}); |
| 68 | +constreq=mockReq('/api/cloudwatch-traces'); |
| 69 | +constres=mockRes(); |
| 70 | + |
| 71 | +awaithandleListCloudWatchTraces(ctx,req,res); |
| 72 | + |
| 73 | +expect(res._status).toBe(400); |
| 74 | +constbody=JSON.parse(res._body); |
| 75 | +expect(body.success).toBe(false); |
| 76 | +expect(body.error).toContain('agentName'); |
| 77 | +expect(body.error).toContain('harnessName'); |
| 78 | +expect(handler).not.toHaveBeenCalled(); |
| 79 | +}); |
| 80 | + |
| 81 | +it('returns 400 when both agentName and harnessName provided',async()=>{ |
| 82 | +consthandler=vi.fn(); |
| 83 | +constctx=mockCtx({onListCloudWatchTraces: handler}); |
| 84 | +constreq=mockReq('/api/cloudwatch-traces?agentName=a&harnessName=h'); |
| 85 | +constres=mockRes(); |
| 86 | + |
| 87 | +awaithandleListCloudWatchTraces(ctx,req,res); |
| 88 | + |
| 89 | +expect(res._status).toBe(400); |
| 90 | +constbody=JSON.parse(res._body); |
| 91 | +expect(body.success).toBe(false); |
| 92 | +expect(body.error).toContain('agentName'); |
| 93 | +expect(body.error).toContain('harnessName'); |
| 94 | +expect(handler).not.toHaveBeenCalled(); |
| 95 | +}); |
| 96 | + |
| 97 | +it('calls handler with agentName and returns traces',async()=>{ |
| 98 | +consttraces=[{traceId: 't1'},{traceId: 't2'}]; |
| 99 | +consthandler=vi.fn().mockResolvedValue({success: true, traces }); |
| 100 | +constctx=mockCtx({onListCloudWatchTraces: handler}); |
| 101 | +constreq=mockReq('/api/cloudwatch-traces?agentName=my-agent'); |
| 102 | +constres=mockRes(); |
| 103 | + |
| 104 | +awaithandleListCloudWatchTraces(ctx,req,res); |
| 105 | + |
| 106 | +expect(res._status).toBe(200); |
| 107 | +expect(handler).toHaveBeenCalledWith('my-agent',undefined,undefined,undefined); |
| 108 | +constbody=JSON.parse(res._body); |
| 109 | +expect(body.success).toBe(true); |
| 110 | +expect(body.traces).toEqual(traces); |
| 111 | +}); |
| 112 | + |
| 113 | +it('calls handler with harnessName',async()=>{ |
| 114 | +consthandler=vi.fn().mockResolvedValue({success: true,traces: []}); |
| 115 | +constctx=mockCtx({onListCloudWatchTraces: handler}); |
| 116 | +constreq=mockReq('/api/cloudwatch-traces?harnessName=my-harness'); |
| 117 | +constres=mockRes(); |
| 118 | + |
| 119 | +awaithandleListCloudWatchTraces(ctx,req,res); |
| 120 | + |
| 121 | +expect(res._status).toBe(200); |
| 122 | +expect(handler).toHaveBeenCalledWith(undefined,'my-harness',undefined,undefined); |
| 123 | +}); |
| 124 | + |
| 125 | +it('returns 500 when handler throws',async()=>{ |
| 126 | +consthandler=vi.fn().mockRejectedValue(newError('boom')); |
| 127 | +constctx=mockCtx({onListCloudWatchTraces: handler}); |
| 128 | +constreq=mockReq('/api/cloudwatch-traces?agentName=my-agent'); |
| 129 | +constres=mockRes(); |
| 130 | + |
| 131 | +awaithandleListCloudWatchTraces(ctx,req,res); |
| 132 | + |
| 133 | +expect(res._status).toBe(500); |
| 134 | +constbody=JSON.parse(res._body); |
| 135 | +expect(body.success).toBe(false); |
| 136 | +expect(body.error).toContain('Failed to list CloudWatch traces'); |
| 137 | +}); |
| 138 | + |
| 139 | +it('returns 400 for invalid startTime',async()=>{ |
| 140 | +consthandler=vi.fn(); |
| 141 | +constctx=mockCtx({onListCloudWatchTraces: handler}); |
| 142 | +constreq=mockReq('/api/cloudwatch-traces?agentName=my-agent&startTime=notanumber'); |
| 143 | +constres=mockRes(); |
| 144 | + |
| 145 | +awaithandleListCloudWatchTraces(ctx,req,res); |
| 146 | + |
| 147 | +expect(res._status).toBe(400); |
| 148 | +constbody=JSON.parse(res._body); |
| 149 | +expect(body.success).toBe(false); |
| 150 | +expect(body.error).toContain('startTime'); |
| 151 | +expect(handler).not.toHaveBeenCalled(); |
| 152 | +}); |
| 153 | +}); |
| 154 | + |
| 155 | +describe('handleGetCloudWatchTrace',()=>{ |
| 156 | +beforeEach(()=>{ |
| 157 | +vi.restoreAllMocks(); |
| 158 | +}); |
| 159 | + |
| 160 | +it('returns 404 when no handler configured',async()=>{ |
| 161 | +constctx=mockCtx(); |
| 162 | +constreq=mockReq('/api/cloudwatch-traces/abc123?agentName=my-agent'); |
| 163 | +constres=mockRes(); |
| 164 | + |
| 165 | +awaithandleGetCloudWatchTrace(ctx,req,res); |
| 166 | + |
| 167 | +expect(res._status).toBe(404); |
| 168 | +constbody=JSON.parse(res._body); |
| 169 | +expect(body.success).toBe(false); |
| 170 | +expect(body.error).toContain('not available'); |
| 171 | +}); |
| 172 | + |
| 173 | +it('returns 400 when traceId is missing',async()=>{ |
| 174 | +consthandler=vi.fn(); |
| 175 | +constctx=mockCtx({onGetCloudWatchTrace: handler}); |
| 176 | +constreq=mockReq('/api/cloudwatch-traces/?agentName=my-agent'); |
| 177 | +constres=mockRes(); |
| 178 | + |
| 179 | +awaithandleGetCloudWatchTrace(ctx,req,res); |
| 180 | + |
| 181 | +expect(res._status).toBe(400); |
| 182 | +constbody=JSON.parse(res._body); |
| 183 | +expect(body.success).toBe(false); |
| 184 | +expect(body.error).toContain('traceId'); |
| 185 | +expect(handler).not.toHaveBeenCalled(); |
| 186 | +}); |
| 187 | + |
| 188 | +it('returns 400 when neither agentName nor harnessName provided',async()=>{ |
| 189 | +consthandler=vi.fn(); |
| 190 | +constctx=mockCtx({onGetCloudWatchTrace: handler}); |
| 191 | +constreq=mockReq('/api/cloudwatch-traces/abc123'); |
| 192 | +constres=mockRes(); |
| 193 | + |
| 194 | +awaithandleGetCloudWatchTrace(ctx,req,res); |
| 195 | + |
| 196 | +expect(res._status).toBe(400); |
| 197 | +constbody=JSON.parse(res._body); |
| 198 | +expect(body.success).toBe(false); |
| 199 | +expect(body.error).toContain('agentName'); |
| 200 | +expect(body.error).toContain('harnessName'); |
| 201 | +expect(handler).not.toHaveBeenCalled(); |
| 202 | +}); |
| 203 | + |
| 204 | +it('returns 500 when handler throws',async()=>{ |
| 205 | +consthandler=vi.fn().mockRejectedValue(newError('boom')); |
| 206 | +constctx=mockCtx({onGetCloudWatchTrace: handler}); |
| 207 | +constreq=mockReq('/api/cloudwatch-traces/abc123?agentName=my-agent'); |
| 208 | +constres=mockRes(); |
| 209 | + |
| 210 | +awaithandleGetCloudWatchTrace(ctx,req,res); |
| 211 | + |
| 212 | +expect(res._status).toBe(500); |
| 213 | +constbody=JSON.parse(res._body); |
| 214 | +expect(body.success).toBe(false); |
| 215 | +expect(body.error).toContain('Failed to get CloudWatch trace'); |
| 216 | +}); |
| 217 | + |
| 218 | +it('calls handler and returns records',async()=>{ |
| 219 | +constrecords=[{record: 'data1'}]; |
| 220 | +consthandler=vi.fn().mockResolvedValue({success: true, records }); |
| 221 | +constctx=mockCtx({onGetCloudWatchTrace: handler}); |
| 222 | +constreq=mockReq('/api/cloudwatch-traces/abc123?agentName=my-agent'); |
| 223 | +constres=mockRes(); |
| 224 | + |
| 225 | +awaithandleGetCloudWatchTrace(ctx,req,res); |
| 226 | + |
| 227 | +expect(res._status).toBe(200); |
| 228 | +expect(handler).toHaveBeenCalledWith('my-agent',undefined,'abc123',undefined,undefined); |
| 229 | +constbody=JSON.parse(res._body); |
| 230 | +expect(body.success).toBe(true); |
| 231 | +expect(body.records).toEqual(records); |
| 232 | +}); |
| 233 | +}); |
0 commit comments