Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { describe, expect, it } from 'bun:test'
import type { Response } from '@cre/generated/capabilities/networking/http/v1alpha/client_pb'
import { describe, expect, it, mock } from 'bun:test'
import { create } from '@bufbuild/protobuf'
import type {
RequestJson,
Response,
} from '@cre/generated/capabilities/networking/http/v1alpha/client_pb'
import {
AttributedSignatureSchema,
type ReportResponse,
ReportResponseSchema,
} from '@cre/generated/sdk/v1alpha/sdk_pb'
import {
ClientCapability,
SendRequester,
} from '@cre/generated-sdk/capabilities/networking/http/v1alpha/client_sdk_gen'
import type { NodeRuntime } from '@cre/index'
import { Report } from '@cre/sdk'
import { getHeader, json, ok, text } from './http-helpers'

// Mock Response object for testing
Expand Down Expand Up @@ -105,3 +120,63 @@ describe('HTTP Helpers', () => {
})
})
})

describe('sendReport extension', () => {
const anyRequest: RequestJson = {
body: 'test',
headers: { a: 'b' },
method: 'GET',
url: 'https://example.com',
}

const anyResponse = createMockResponse(200, new Uint8Array(), { a: 'b' })

const anyReportResponse = create(ReportResponseSchema, {
configDigest: new Uint8Array([1, 2, 3]),
seqNr: 101n,
reportContext: new Uint8Array([4, 5, 6]),
rawReport: new Uint8Array([7, 8, 9]),
sigs: [
create(AttributedSignatureSchema, {
signature: new Uint8Array([10, 11, 12]),
signerId: 13,
}),
create(AttributedSignatureSchema, {
signature: new Uint8Array([16, 17, 18]),
signerId: 14,
}),
],
})
const anyReport = new Report(anyReportResponse)

// safe for this test as we don't use the runtime itself
const anyRuntime = {} as NodeRuntime<unknown>

const produceReport = (reportResponse: ReportResponse) => {
expect(reportResponse).toEqual(anyReportResponse)
return anyRequest
}

class ClientCapabilityMock extends ClientCapability {
// biome-ignore lint/suspicious/noExplicitAny: Test mock needs to override complex overloaded method signatures. Using 'any' bypasses TypeScript's strict overload compatibility checks that would otherwise require redeclaring all overloaded signatures exactly.
sendRequest(...args: any[]): any {
expect(2).toEqual(args.length)
const [runtime, input] = args
expect(runtime).toBe(anyRuntime)
expect(input).toBe(anyRequest)
return { result: () => anyResponse }
}
}

it('ClientCapability should call the transform function and use the result', () => {
const clientCapability = new ClientCapabilityMock()
const response = clientCapability.sendReport(anyRuntime, anyReport, produceReport)
expect(response.result()).toBe(anyResponse)
})

it('SendRequester should call the transform function and use the result', () => {
const sendRequester = new SendRequester(anyRuntime, new ClientCapabilityMock())
const response = sendRequester.sendReport(anyReport, produceReport)
expect(response.result()).toBe(anyResponse)
})
})
118 changes: 117 additions & 1 deletion packages/cre-sdk/src/sdk/utils/capabilities/http/http-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
import type { Response } from '@cre/generated/capabilities/networking/http/v1alpha/client_pb'
import type {
Request,
RequestJson,
Response,
} from '@cre/generated/capabilities/networking/http/v1alpha/client_pb'
import type { ReportResponse } from '@cre/generated/sdk/v1alpha/sdk_pb'
import type {
ClientCapability,
SendRequester,
} from '@cre/generated-sdk/capabilities/networking/http/v1alpha/client_sdk_gen'
import type { NodeRuntime } from '@cre/sdk'
import type { Report } from '@cre/sdk/report'

/**
* HTTP Response Helper Functions
Expand Down Expand Up @@ -159,3 +170,108 @@ export function ok(
return responseOrFn.statusCode >= 200 && responseOrFn.statusCode < 300
}
}

// ============================================================================
// SendReport Helper Methods for ClientCapability and SendRequester
// ============================================================================

/**
* SendReport functions the same as SendRequest, but takes a Report and a function
* to convert the inner ReportResponse to a Request.
* Note that caching is limited as reports may contain different sets of signatures
* on different nodes, leading to a cache miss.
*
* @param runtime - The runtime instance
* @param report - The Report to process
* @param fn - Function to convert ReportResponse to Request
* @returns Response result function
*/
function sendReport(
this: ClientCapability,
runtime: NodeRuntime<unknown>,
report: Report,
fn: (reportResponse: ReportResponse) => Request | RequestJson,
): { result: () => Response } {
const rawReport = report.x_generatedCodeOnly_unwrap()
const request = fn(rawReport)
return this.sendRequest(runtime, request)
}

/**
* SendReport functions the same as SendRequest, but takes a Report and a function
* to convert the inner ReportResponse to a Request.
* Note that caching is limited as reports may contain different sets of signatures
* on different nodes, leading to a cache miss.
*
* @param report - The Report to process
* @param fn - Function to convert ReportResponse to Request
* @returns Response result function
*/
function sendRequesterSendReport(
this: SendRequester,
report: Report,
fn: (reportResponse: ReportResponse) => Request | RequestJson,
): { result: () => Response } {
const rawReport = report.x_generatedCodeOnly_unwrap()
const request = fn(rawReport)
return this.sendRequest(request)
}

// ============================================================================
// Prototype Extensions
// ============================================================================

// Import the actual classes for prototype extension
import {
ClientCapability as ClientCapabilityClass,
SendRequester as SendRequesterClass,
} from '@cre/generated-sdk/capabilities/networking/http/v1alpha/client_sdk_gen'

// Extend ClientCapability prototype
ClientCapabilityClass.prototype.sendReport = sendReport

// Extend SendRequester prototype
SendRequesterClass.prototype.sendReport = sendRequesterSendReport

// ============================================================================
// Type Declarations for Prototype Extensions
// ============================================================================

// Augment the module declarations to include the new methods
declare module '@cre/generated-sdk/capabilities/networking/http/v1alpha/client_sdk_gen' {
interface ClientCapability {
/**
* SendReport functions the same as SendRequest, but takes a Report and a function
* to convert the inner ReportResponse to a Request.
* Note that caching is limited as reports may contain different sets of signatures
* on different nodes, leading to a cache miss.
*
* @param runtime - The runtime instance
* @param report - The Report to process
* @param fn - Function to convert ReportResponse to Request
* @returns Response result function
*/
sendReport(
runtime: NodeRuntime<unknown>,
report: Report,
fn: (reportResponse: ReportResponse) => Request | RequestJson,
): { result: () => Response }
}

interface SendRequester {
/**
* SendReport functions the same as SendRequest, but takes a Report and a function
* to convert the inner ReportResponse to a Request.
* Note that caching is limited as reports may contain different sets of signatures
* on different nodes, leading to a cache miss.
*
* @param report - The Report to process
* @param fn - Function to convert ReportResponse to Request
* @returns Response result function
*/
sendReport(
report: Report,
fn: (reportResponse: ReportResponse) => Request | RequestJson,
): { result: () => Response }
}
}