Skip to content

Commit

Permalink
Move verification tests into this repo
Browse files Browse the repository at this point in the history
Fixes #122
  • Loading branch information
jackkleeman committed May 31, 2023
1 parent 3f9a3d4 commit 13e2a76
Show file tree
Hide file tree
Showing 12 changed files with 624 additions and 21 deletions.
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ allprojects {
}
}

buildscript {
// required for m1 mac
configurations { classpath { resolutionStrategy { force("net.java.dev.jna:jna:5.7.0") } } }
}

subprojects {
apply(plugin = "java")
apply(plugin = "kotlin")
Expand Down
1 change: 1 addition & 0 deletions contracts/src/main/proto/interpreter.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ message TestParams {
string seed = 1;
int32 width = 2;
int32 depth = 3;
optional int32 max_sleep_millis = 4;
}

message Key {
Expand Down
2 changes: 2 additions & 0 deletions services/node-services/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@restatedev:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${GH_PACKAGE_READ_ACCESS_TOKEN}
25 changes: 12 additions & 13 deletions services/node-services/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
FROM node:18 AS build
ARG NPM_TOKEN
ARG GH_PACKAGE_READ_ACCESS_TOKEN

WORKDIR /usr/src/app
COPY . .

RUN echo "//npm.pkg.github.com/:_authToken=$NPM_TOKEN\n" >> .npmrc && \
echo "@restatedev:registry=https://npm.pkg.github.com/" >> .npmrc && \
npm ci && \
npm run build && \
rm -f .npmrc
RUN npm ci
RUN npm run build

FROM node:18 as prod
ARG NPM_TOKEN
ARG GH_PACKAGE_READ_ACCESS_TOKEN
WORKDIR /usr/src/app

# Install app dependencies
COPY package*.json *.tgz ./
RUN echo "//npm.pkg.github.com/:_authToken=$NPM_TOKEN\n" >> .npmrc && \
echo "@restatedev:registry=https://npm.pkg.github.com/" >> .npmrc && \
npm ci --production && \
rm -f .npmrc
COPY package*.json *.tgz .npmrc ./
RUN npm ci --production

COPY --from=build /usr/src/app/dist /usr/src/app/dist

FROM node:18

# Use a new stage so that the build-arg GH_PACKAGE_READ_ACCESS_TOKEN isn't leaked into the final image history
COPY --from=prod /usr/src/app/ /usr/src/app/

# Install Tini
RUN apt-get update && apt-get -y install tini

EXPOSE 8080
ENTRYPOINT ["tini", "--"]
CMD ["node", "/usr/src/app/dist/app.js"]
CMD ["node", "/usr/src/app/dist/app.js"]
3 changes: 0 additions & 3 deletions services/node-services/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ To build the docker image:
$ gradle :services:node-services:dockerBuild
```

> **Note**
> The `GH_PACKAGE_READ_ACCESS_TOKEN` will be used as `NPM_TOKEN` when building the docker image through gradle
## Run proto code generation

To re-gen the `generated` directory:
Expand Down
3 changes: 2 additions & 1 deletion services/node-services/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ tasks.register<Copy>("prepareDockerBuild") {
".dockerignore",
".eslintignore",
".eslintrc.json",
".npmrc",
"package.json",
"package-lock.json",
"tsconfig.json",
Expand All @@ -39,7 +40,7 @@ tasks.register<Copy>("prepareDockerBuild") {
tasks.create<DockerBuildImage>("dockerBuild") {
dependsOn("prepareDockerBuild")
images.add("restatedev/e2e-node-services")
buildArgs.put("NPM_TOKEN", System.getenv("GH_PACKAGE_READ_ACCESS_TOKEN"))
buildArgs.put("GH_PACKAGE_READ_ACCESS_TOKEN", System.getenv("GH_PACKAGE_READ_ACCESS_TOKEN"))
}

tasks.named("check") {
Expand Down
13 changes: 13 additions & 0 deletions services/node-services/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions services/node-services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
"dependencies": {
"@restatedev/restate-sdk": "1.0.26",
"protobufjs": "^7.2.2",
"seedrandom": "^3.0.5",
"ts-proto": "^1.140.0",
"uuid": "^9.0.0"
},
"devDependencies": {
"@bufbuild/buf": "1.15.0",
"@types/seedrandom": "^3.0.5",
"@types/uuid": "^9.0.1",
"@typescript-eslint/eslint-plugin": "^5.53.0",
"@typescript-eslint/parser": "^5.53.0",
Expand Down
23 changes: 23 additions & 0 deletions services/node-services/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { protoMetadata as receiverProtoMetadata } from "./generated/receiver";
import { protoMetadata as listProtoMetadata } from "./generated/list";
import { protoMetadata as errorsProtoMetadata } from "./generated/errors";
import { protoMetadata as nonDeterminismProtoMetadata } from "./generated/non_determinism";
import { protoMetadata as verifierProtoMetadata } from "./generated/verifier";
import { protoMetadata as interpreterProtoMetadata } from "./generated/interpreter";
import { CounterService, CounterServiceFQN } from "./counter";
import { ListService, ListServiceFQN } from "./collections";
import { FailingService, FailingServiceFQN } from "./errors";
Expand All @@ -17,6 +19,11 @@ import {
NonDeterministicService,
NonDeterministicServiceFQN,
} from "./non_determinism";
import { CommandVerifierService, CommandVerifierServiceFQN } from "./verifier";
import {
CommandInterpreterService,
CommandInterpreterServiceFQN,
} from "./interpreter";

let serverBuilder = restate.createServer();

Expand Down Expand Up @@ -77,6 +84,22 @@ const services = new Map<string, restate.ServiceOpts>([
instance: new FailingService(),
},
],
[
CommandVerifierServiceFQN,
{
descriptor: verifierProtoMetadata,
service: "CommandVerifier",
instance: new CommandVerifierService(),
},
],
[
CommandInterpreterServiceFQN,
{
descriptor: interpreterProtoMetadata,
service: "CommandInterpreter",
instance: new CommandInterpreterService(),
},
],
]);

console.log(services.keys());
Expand Down
189 changes: 189 additions & 0 deletions services/node-services/src/interpreter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import { RestateContext, useContext } from "@restatedev/restate-sdk";
import {
BackgroundCallRequest,
CallRequest,
ClearRequest,
Command_AsyncCall,
Command_AsyncCallAwait,
Command_BackgroundCall,
Command_IncrementState,
Command_Sleep,
Command_SyncCall,
CommandInterpreter,
CommandInterpreterClientImpl,
Commands,
Empty,
Key,
protobufPackage,
TestParams,
VerificationRequest,
VerificationResult,
} from "./generated/interpreter";

export const CommandInterpreterServiceFQN =
protobufPackage + ".CommandInterpreter";

export class CommandInterpreterService implements CommandInterpreter {
async call(request: CallRequest): Promise<Empty> {
return this.eitherCall(request.key, request.commands);
}

async backgroundCall(request: BackgroundCallRequest): Promise<Empty> {
return this.eitherCall(request.key, request.commands);
}

async eitherCall(
key: Key | undefined,
commands: Commands | undefined
): Promise<Empty> {
if (!commands?.command) {
throw new Error("CallRequest with no commands");
}
if (!key) {
throw new Error("CallRequest with no key");
}
if (!key.params) {
throw new Error("CallRequest with no test parameters");
}
const ctx = useContext(this);
const client = new CommandInterpreterClientImpl(ctx);
const pending_calls = new Map<number, Promise<Empty>>();

for (const c of commands.command) {
switch (true) {
case c.increment !== undefined:
await this._increment(ctx, c.increment as Command_IncrementState);
break;
case c.syncCall !== undefined:
await this._syncCall(
ctx,
client,
key.params,
c.syncCall as Command_SyncCall
);
break;
case c.asyncCall !== undefined:
this._asyncCall(
ctx,
client,
pending_calls,
key.params,
c.asyncCall as Command_AsyncCall
);
break;
case c.asyncCallAwait !== undefined:
await this._asyncCallAwait(
ctx,
pending_calls,
c.asyncCallAwait as Command_AsyncCallAwait
);
break;
case c.backgroundCall !== undefined:
await this._backgroundCall(
ctx,
client,
key.params,
c.backgroundCall as Command_BackgroundCall
);
break;
case c.sleep !== undefined:
await this._sleep(ctx, c.sleep as Command_Sleep);
break;
default:
// should be unreachable
throw new Error("Empty Command in CallRequest");
}
}

return Empty.create({});
}

async _increment(
ctx: RestateContext,
request: Command_IncrementState
): Promise<void> {
const counter = (await ctx.get<number>("counter")) || 0;
return ctx.set("counter", counter + 1);
}

async _syncCall(
ctx: RestateContext,
client: CommandInterpreterClientImpl,
params: TestParams,
request: Command_SyncCall
): Promise<void> {
await client.call(
CallRequest.create({
key: { params, target: request.target },
commands: request.commands,
})
);
}

_asyncCall(
ctx: RestateContext,
client: CommandInterpreterClientImpl,
pending_calls: Map<number, Promise<Empty>>,
params: TestParams,
request: Command_AsyncCall
) {
pending_calls.set(
request.callId,
client.call(
CallRequest.create({
key: { params, target: request.target },
commands: request.commands,
})
)
);
}

async _asyncCallAwait(
ctx: RestateContext,
pending_calls: Map<number, Promise<Empty>>,
request: Command_AsyncCallAwait
): Promise<void> {
const p = pending_calls.get(request.callId);
if (p === undefined) {
throw new Error("Unrecognised CallID in AsyncCallAwait command");
}
await p;
return;
}

async _backgroundCall(
ctx: RestateContext,
client: CommandInterpreterClientImpl,
params: TestParams,
request: Command_BackgroundCall
): Promise<void> {
return ctx.oneWayCall(() =>
client.backgroundCall(
BackgroundCallRequest.create({
key: { params, target: request.target },
commands: request.commands,
})
)
);
}

async _sleep(ctx: RestateContext, request: Command_Sleep): Promise<void> {
return ctx.sleep(request.milliseconds);
}

async verify(request: VerificationRequest): Promise<VerificationResult> {
const ctx = useContext(this);
return VerificationResult.create({
expected: request.expected,
actual: (await ctx.get<number>("counter")) || 0,
});
}

async clear(request: ClearRequest): Promise<Empty> {
const ctx = useContext(this);

await ctx.clear("counter");

return Empty.create({});
}
}
Loading

0 comments on commit 13e2a76

Please sign in to comment.