Skip to content

Commit

Permalink
Making typed function handlers easier (#33)
Browse files Browse the repository at this point in the history
* adding types for Slack Function handlers

* adding tests and handling cases with no input or output params

* adding tasks

* clarify things a little

* adding better assertions on function handler type tests
  • Loading branch information
selfcontained authored Apr 27, 2022
1 parent 05d87a5 commit c13d017
Show file tree
Hide file tree
Showing 10 changed files with 448 additions and 15 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
.coverage
6 changes: 6 additions & 0 deletions deno.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"tasks": {
"test": "deno test src && deno fmt --check src && deno lint src",
"coverage": "deno test --allow-read --coverage=.coverage && deno coverage --exclude=fixtures|test .coverage"
}
}
1 change: 1 addition & 0 deletions src/dev_deps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export {
assertEquals,
assertExists,
assertStrictEquals,
} from "https://deno.land/[email protected]/testing/asserts.ts";
122 changes: 122 additions & 0 deletions src/functions/base_function_handler_type_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { assertEquals } from "../dev_deps.ts";
import { SlackFunctionTester } from "./function_tester.ts";
import { BaseSlackFunctionHandler } from "./types.ts";

// These tests are to ensure our Function Handler types are supporting the use cases we want to
// Any "failures" here will most likely be reflected in Type errors

Deno.test("BaseSlackFunctionHandler types", () => {
type Inputs = {
in: string;
};
type Outputs = {
out: string;
};
const handler: BaseSlackFunctionHandler<Inputs, Outputs> = (
{ inputs },
) => {
return {
outputs: {
out: inputs.in,
},
};
};
const { createContext } = SlackFunctionTester("test");
const inputs = { in: "test" };
const result = handler(createContext({ inputs }));
assertEquals(result.outputs?.out, inputs.in);
});

Deno.test("BaseSlackFunctionHandler with empty inputs and outputs", () => {
type Inputs = Record<never, never>;
type Outputs = Record<never, never>;
const handler: BaseSlackFunctionHandler<Inputs, Outputs> = () => {
return {
outputs: {},
};
};
const { createContext } = SlackFunctionTester("test");
const result = handler(createContext({ inputs: {} }));
assertEquals(result.outputs, {});
});

Deno.test("BaseSlackFunctionHandler with undefined inputs and outputs", () => {
type Inputs = undefined;
type Outputs = undefined;
const handler: BaseSlackFunctionHandler<Inputs, Outputs> = () => {
return {
outputs: {},
};
};
const { createContext } = SlackFunctionTester("test");
const result = handler(createContext({ inputs: undefined }));
assertEquals(result.outputs, {});
});

Deno.test("BaseSlackFunctionHandler with inputs and empty outputs", () => {
type Inputs = {
in: string;
};
type Outputs = Record<never, never>;
const handler: BaseSlackFunctionHandler<Inputs, Outputs> = ({ inputs }) => {
const _test = inputs.in;

return {
outputs: {},
};
};
const { createContext } = SlackFunctionTester("test");
const inputs = { in: "test" };
const result = handler(createContext({ inputs }));
assertEquals(result.outputs, {});
});

Deno.test("BaseSlackFunctionHandler with empty inputs and outputs", () => {
type Inputs = Record<never, never>;
type Outputs = {
out: string;
};
const handler: BaseSlackFunctionHandler<Inputs, Outputs> = () => {
return {
outputs: {
out: "test",
},
};
};
const { createContext } = SlackFunctionTester("test");
const result = handler(createContext({ inputs: {} }));
assertEquals(result.outputs?.out, "test");
});

Deno.test("BaseSlackFunctionHandler with any inputs and any outputs", () => {
// deno-lint-ignore no-explicit-any
const handler: BaseSlackFunctionHandler<any, any> = ({ inputs }) => {
return {
outputs: {
out: inputs.in,
},
};
};
const { createContext } = SlackFunctionTester("test");
const inputs = { in: "test" };
const result = handler(createContext({ inputs }));
assertEquals(result.outputs?.out, inputs.in);
});

Deno.test("BaseSlackFunctionHandler with set inputs and any outputs", () => {
type Inputs = {
in: string;
};
// deno-lint-ignore no-explicit-any
const handler: BaseSlackFunctionHandler<Inputs, any> = ({ inputs }) => {
return {
outputs: {
out: inputs.in,
},
};
};
const { createContext } = SlackFunctionTester("test");
const inputs = { in: "test" };
const result = handler(createContext({ inputs }));
assertEquals(result.outputs?.out, inputs.in);
});
10 changes: 7 additions & 3 deletions src/functions/function_tester.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { FunctionContext } from "./types.ts";
type SlackFunctionTesterArgs<InputParameters> =
& Partial<FunctionContext<InputParameters>>
type SlackFunctionTesterArgs<
InputParameters,
> =
& Partial<
FunctionContext<InputParameters>
>
& {
inputs: InputParameters;
};
Expand All @@ -15,7 +19,7 @@ export const SlackFunctionTester = (callbackId: string) => {
const ts = new Date();

return {
inputs: args.inputs,
inputs: (args.inputs || {}) as InputParameters,
env: args.env || {},
token: args.token || "slack-function-test-token",
event: args.event || {
Expand Down
13 changes: 13 additions & 0 deletions src/functions/function_tester_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,16 @@ Deno.test("SlackFunctionTester.createContext", () => {
assertEquals(ctx.event.type, "function_executed");
assertEquals(ctx.event.function.callback_id, callbackId);
});

Deno.test("SlackFunctionTester.createContext with empty inputs", () => {
const callbackId = "my_callback_id";
const { createContext } = SlackFunctionTester(callbackId);

const ctx = createContext({ inputs: {} });

assertEquals(ctx.inputs, {});
assertEquals(ctx.env, {});
assertEquals(typeof ctx.token, "string");
assertEquals(ctx.event.type, "function_executed");
assertEquals(ctx.event.function.callback_id, callbackId);
});
188 changes: 188 additions & 0 deletions src/functions/slack_function_handler_type_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { assertEquals } from "../dev_deps.ts";
import { SlackFunctionTester } from "./function_tester.ts";
import { DefineFunction } from "./mod.ts";
import { SlackFunctionHandler } from "./types.ts";

// These tests are to ensure our Function Handler types are supporting the use cases we want to
// Any "failures" here will most likely be reflected in Type errors

Deno.test("SlackFunctionHandler with inputs and outputs", () => {
const TestFn = DefineFunction({
callback_id: "test",
title: "test fn",
source_file: "test.ts",
input_parameters: {
properties: {
in: {
type: "string",
},
},
required: ["in"],
},
output_parameters: {
properties: {
out: {
type: "string",
},
},
required: ["out"],
},
});
const handler: SlackFunctionHandler<typeof TestFn.definition> = (
{ inputs },
) => {
return {
outputs: {
out: inputs.in,
},
};
};
const { createContext } = SlackFunctionTester("test");
const inputs = { in: "test" };
const result = handler(createContext({ inputs }));
assertEquals(result.outputs?.out, inputs.in);
});

Deno.test("SlackFunctionHandler with optional input", () => {
const TestFn = DefineFunction({
callback_id: "test",
title: "test fn",
source_file: "test.ts",
input_parameters: {
properties: {
in: {
type: "string",
},
},
required: [],
},
output_parameters: {
properties: {
out: {
type: "string",
},
},
required: ["out"],
},
});
const handler: SlackFunctionHandler<typeof TestFn.definition> = (
{ inputs },
) => {
return {
outputs: {
out: inputs.in || "default",
},
};
};
const { createContext } = SlackFunctionTester("test");
const inputs = {};
const result = handler(createContext({ inputs }));
assertEquals(result.outputs?.out, "default");
});

Deno.test("SlackFunctionHandler with no inputs or outputs", () => {
const TestFn = DefineFunction({
callback_id: "test",
title: "test fn",
source_file: "test.ts",
});
const handler: SlackFunctionHandler<typeof TestFn.definition> = () => {
return {
outputs: {},
};
};
const { createContext } = SlackFunctionTester("test");
const result = handler(createContext({ inputs: {} }));
assertEquals(result.outputs, {});
});

Deno.test("SlackFunctionHandler with undefined inputs and outputs", () => {
const TestFn = DefineFunction({
callback_id: "test",
title: "test fn",
source_file: "test.ts",
input_parameters: undefined,
output_parameters: undefined,
});
const handler: SlackFunctionHandler<typeof TestFn.definition> = () => {
return {
outputs: {},
};
};
const { createContext } = SlackFunctionTester("test");
const result = handler(createContext({ inputs: {} }));
assertEquals(result.outputs, {});
});

Deno.test("SlackFunctionHandler with empty inputs and outputs", () => {
const TestFn = DefineFunction({
callback_id: "test",
title: "test fn",
source_file: "test.ts",
input_parameters: { properties: {}, required: [] },
output_parameters: { properties: {}, required: [] },
});
const handler: SlackFunctionHandler<typeof TestFn.definition> = () => {
return {
outputs: {},
};
};
const { createContext } = SlackFunctionTester("test");
const result = handler(createContext({ inputs: {} }));
assertEquals(result.outputs, {});
});

Deno.test("SlackFunctionHandler with only inputs", () => {
const TestFn = DefineFunction({
callback_id: "test",
title: "test fn",
source_file: "test.ts",
input_parameters: {
properties: {
in: {
type: "string",
},
},
required: ["in"],
},
});
const handler: SlackFunctionHandler<typeof TestFn.definition> = (
{ inputs },
) => {
const _test = inputs.in;

return {
outputs: {},
};
};
const { createContext } = SlackFunctionTester("test");
const inputs = { in: "test" };
const result = handler(createContext({ inputs }));
assertEquals(result.outputs, {});
});

Deno.test("SlackFunctionHandler with only outputs", () => {
const TestFn = DefineFunction({
callback_id: "test",
title: "test fn",
source_file: "test.ts",
output_parameters: {
properties: {
out: {
type: "string",
},
},
required: ["out"],
},
});
const handler: SlackFunctionHandler<typeof TestFn.definition> = () => {
return {
outputs: {
out: "test",
},
};
};
const { createContext } = SlackFunctionTester("test");
const result = handler(createContext({ inputs: {} }));
assertEquals(result.outputs?.out, "test");
});
Loading

0 comments on commit c13d017

Please sign in to comment.