Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move verification tests into this repo #123

Merged
merged 1 commit into from
May 31, 2023
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
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") } } }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tillrohrmann can you double check this? I don't remember you ever needed this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need this fix in 5.7.0 java-native-access/jna#1238

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's quite an old bug, i wonder which dep is bringing this in

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

|         +--- org.jetbrains.kotlin:kotlin-compiler-embeddable:1.8.10
|         |    +--- org.jetbrains.kotlin:kotlin-daemon-embeddable:1.8.10
|         |    +--- org.jetbrains.intellij.deps:trove4j:1.0.20200330
|         |    \--- net.java.dev.jna:jna:5.6.0 -> 5.7.0

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh ok i'll try to see if this goes away when bumping kotlin

}

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/
Comment on lines +22 to +23
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this!


# 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