Skip to content

Commit

Permalink
Header serializer tests (#2574)
Browse files Browse the repository at this point in the history
* Initial header serialization

* Update tests
  • Loading branch information
joheredi committed Jun 13, 2024
1 parent d1bca0f commit 365517e
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 24 deletions.
14 changes: 12 additions & 2 deletions packages/typespec-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,19 @@
"description": "An experimental TypeSpec emitter for TypeScript RLC",
"main": "dist/src/index.js",
"type": "module",
"exports": {
".": {
"default": "./dist/src/index.js",
"types": "./dist/src/index.d.ts"
},
"./testing": {
"default": "./dist/src/testing/index.js",
"types": "./dist/src/testing/index.d.ts"
}
},
"scripts": {
"test-next": "vitest ./test-next",
"test-next:coverage": "vitest ./test-next --coverage",
"test-next": "vitest run ./test-next",
"test-next:coverage": "vitest run ./test-next --coverage",
"clean": "rimraf ./dist ./typespec-output",
"build": "tsc -p .",
"test": "npm run unit-test && npm run integration-test-ci",
Expand Down
20 changes: 20 additions & 0 deletions packages/typespec-ts/src/modular/serialization/collectionUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { CollectionFormat } from "@azure-tools/typespec-client-generator-core";

export function getCollectionSeparator(
collectionFormat?: CollectionFormat
): string {
switch (collectionFormat) {
case "csv":
return ",";
case "ssv":
return " ";
case "tsv":
return "\t";
case "pipes":
return "|";
case "multi":
return "&";
default:
return ",";
}
}
28 changes: 28 additions & 0 deletions packages/typespec-ts/src/modular/serialization/serializeHeaders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { SdkHeaderParameter } from "@azure-tools/typespec-client-generator-core";
import {
SerializeTypeOptions,
serializeArray,
serializeType
} from "./serializers.js";
import { SerializerOutput } from "./util.js";
import { getCollectionSeparator } from "./collectionUtils.js";

export function serializeHeader(
options: SerializeTypeOptions<
SdkHeaderParameter & {
kind: "header";
}
>
): SerializerOutput {
const { type } = options;

if (type.type.kind === "array") {
const serializedArray = serializeArray({ ...options, type: type.type });
const separator = JSON.stringify(
getCollectionSeparator(type.collectionFormat)
);
return `${serializedArray}.join(${separator})`;
}

return serializeType({ ...options, type: type.type });
}
7 changes: 4 additions & 3 deletions packages/typespec-ts/src/modular/serialization/serializers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import {
SerializerMap,
SerializerOutput
} from "./util.js";
import { serializeHeader } from "./serializeHeaders.js";

interface SerializeTypeOptions<
export interface SerializeTypeOptions<
TCGCType extends SdkType | SdkModelPropertyType
> {
dpgContext: SdkContext;
Expand Down Expand Up @@ -145,7 +146,7 @@ function getSerializeHandler<TCGCType extends SdkType | SdkModelPropertyType>(
query: placeholder,
path: placeholder,
body: placeholder,
header: placeholder
header: serializeHeader
};
const handler = handlers[kind];
return handler as any;
Expand Down Expand Up @@ -192,7 +193,7 @@ function serializeDatetime(
}
}

function serializeArray(
export function serializeArray(
options: SerializeTypeOptions<SdkArrayType>
): SerializerOutput {
const { dpgContext, functionType, serializerMap, type, valueExpr } = options;
Expand Down
10 changes: 10 additions & 0 deletions packages/typespec-ts/src/testing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {
createTestLibrary,
findTestPackageRoot
} from "@typespec/compiler/testing";

export const TypespecTsTestLibrary = createTestLibrary({
name: "@azure-tools/typespec-ts",
// Set this to the absolute path to the root of the package. (e.g. in this case this file would be compiled to ./dist/src/testing/index.js)
packageRoot: await findTestPackageRoot(import.meta.url)
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { describe, it, assert, beforeEach } from "vitest";
import { createMyTestRunner, createTcgcContext } from "./test-hots.js";
import { SdkHeaderParameter } from "@azure-tools/typespec-client-generator-core";
import { serializeHeader } from "../../../src/modular/serialization/serializeHeaders.js";

import { EmitContext, UsageFlags } from "@typespec/compiler";
import { BasicTestRunner } from "@typespec/compiler/testing";
import { SerializeTypeOptions } from "../../../src/modular/serialization/serializers.js";

describe("headerSerializer", () => {
let runner: BasicTestRunner;

beforeEach(async () => {
runner = await createMyTestRunner();
});

it("should handle a simple header", async () => {
const headerParam = await getSdkHeader(`@header param: string`, runner);
const result = serializeHeader(headerParam);

assert.equal(result, "param");
});

it("should handle a simple array header", async () => {
const headerParam = await getSdkHeader(
`@header({format: "csv"}) param: string[]`,
runner
);
const result = serializeHeader(headerParam);

assert.equal(result, 'param.join(",")');
});

it("should handle a numeric array header", async () => {
const headerParam = await getSdkHeader(
`@header({format: "csv"}) param: int32[]`,
runner
);
const result = serializeHeader(headerParam);

assert.equal(result, `param.join(",")`);
});

it("should handle a boolean array header", async () => {
const headerParam = await getSdkHeader(
`@header({format: "csv"}) param: boolean[]`,
runner
);
const result = serializeHeader(headerParam);

assert.equal(result, `param.join(",")`);
});

// Enable when parameter typename is fixed
it.skip("should handle a datetime array header", async () => {
const headerParam = await getSdkHeader(
`@header({format: "csv"}) param: utcDateTime[]`,
runner
);
const result = serializeHeader(headerParam);

assert.equal(result, `param.map((e: Date)=>((e).toISOString())).join(",")`);
});

it("should handle a string array header with tsv format", async () => {
const headerParam = await getSdkHeader(
`@header({format: "tsv"}) param: string[]`,
runner
);
const result = serializeHeader(headerParam);

assert.equal(result, `param.join("\\t")`);
});
});

type HeaderType = SerializeTypeOptions<
SdkHeaderParameter & {
kind: "header";
}
>;

async function getSdkHeader(
code: string,
runner: BasicTestRunner
): Promise<HeaderType> {
const template = ` @service({
title: "Widget Service",
})
namespace DemoService;
@route("/widgets")
interface Widgets {
@test op foo(${code}): string;
}`;

await runner.compile(template);

const context: EmitContext = {
program: runner.program
} as any;

const sdkContext = createTcgcContext(context);
const headerParam = sdkContext.experimental_sdkPackage.clients[0].methods[0]
.parameters[0] as unknown as SdkHeaderParameter;

return {
dpgContext: sdkContext,
type: headerParam,
valueExpr: headerParam.name,
functionType: UsageFlags.Input
} as any;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
SdkContext,
createSdkContext
} from "@azure-tools/typespec-client-generator-core";
import { EmitContext } from "@typespec/compiler";
import { createTestHost, createTestWrapper } from "@typespec/compiler/testing";
import { HttpTestLibrary } from "@typespec/http/testing";

export async function createMyTestHost() {
return createTestHost({
libraries: [HttpTestLibrary]
});
}

export async function createMyTestRunner() {
const host = await createMyTestHost();
return createTestWrapper(host, { autoUsings: ["TypeSpec.Http"] });
}

export function createTcgcContext(
context: EmitContext<Record<string, any>>
): SdkContext {
const tcgcSettings = {
"generate-protocol-methods": true,
"generate-convenience-methods": true,
"flatten-union-as-enum": false,
emitters: [
{
main: "@azure-tools/typespec-ts",
metadata: { name: "@azure-tools/typespec-ts" }
}
]
};

const contextForTcgc = {
...context,
options: {
...context.options,
...tcgcSettings
}
};

return createSdkContext(contextForTcgc);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, it, expect } from "vitest";
import { getCollectionSeparator } from "../../../src/modular/serialization/collectionUtils.js";

describe("getCollectionSeparator", () => {
it("should return ',' for csv", () => {
const result = getCollectionSeparator("csv");
expect(result).toBe(",");
});

it("should return ' ' for ssv", () => {
const result = getCollectionSeparator("ssv");
expect(result).toBe(" ");
});

it("should return '\\t' for tsv", () => {
const result = getCollectionSeparator("tsv");
expect(result).toBe("\t");
});

it("should return '|' for pipes", () => {
const result = getCollectionSeparator("pipes");
expect(result).toBe("|");
});

it("should return '&' for multi", () => {
const result = getCollectionSeparator("multi");
expect(result).toBe("&");
});

it("should return ',' by default", () => {
const result = getCollectionSeparator(undefined);
expect(result).toBe(",");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,21 @@ import {
getRLCLroLogicalResponse,
getRLCResponseType,
getSendPrivateFunction
} from "../../src/modular/serialization/operationHelpers.js";
} from "../../../src/modular/serialization/operationHelpers.js";
import {
Imports as RuntimeImports,
OperationResponse
} from "@azure-tools/rlc-common";
import { SerializerMap } from "../../src/modular/serialization/util.js";
import { SerializerMap } from "../../../src/modular/serialization/util.js";
import {
Operation,
Parameter,
Response
} from "../../src/modular/modularCodeModel.js";
} from "../../../src/modular/modularCodeModel.js";
import {
SdkModelType,
SdkType,
UsageFlags
} from "@azure-tools/typespec-client-generator-core";
import { a } from "vitest/dist/suite-IbNSsUWN.js";

describe("Operation Helpers", () => {
describe("getRLCResponseType", () => {
Expand Down Expand Up @@ -373,8 +371,7 @@ describe("Operation Helpers", () => {
"if(isUnexpected(result)){",
"throw createRestError(result);",
"}",
//TODO: Update when this is implemented
'return (()=>{throw Error("Not implemented.")})()'
"return {...(result.body)}"
]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,9 @@ import {
getParameterTypePropertyName,
getRLCTypeId,
getReturnTypePropertyName
} from "../../src/modular/serialization/util.js";
} from "../../../src/modular/serialization/util.js";
import { UsageFlags } from "@typespec/compiler";
import {
describe,
it,
afterEach,
vi,
MockedObject,
MockedFunction,
Mock
} from "vitest";
import { getLibraryName } from "@azure-tools/typespec-client-generator-core";
import { beforeEach } from "node:test";
import { describe, it, afterEach, vi } from "vitest";

// Mock the module
vi.mock("@azure-tools/typespec-client-generator-core", async () => {
Expand Down

0 comments on commit 365517e

Please sign in to comment.