Skip to content

Commit 5909ebc

Browse files
Add docker
1 parent fe7957c commit 5909ebc

File tree

34 files changed

+1455
-451
lines changed

34 files changed

+1455
-451
lines changed

ballerina-tests/build.gradle

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,36 @@ task initializeVariables {
9292

9393
def ballerinaPackagePath = "ballerina"
9494

95+
// ---------------------------------------------------------------------------
96+
// Build Docker images for the mock catalog API server and the OAuth2 token
97+
// server. Both images are built from standalone Ballerina programs under
98+
// ballerina-tests/mock-server/ and ballerina-tests/token-server/.
99+
// The images are used by the test suite to spin up one container per port.
100+
// ---------------------------------------------------------------------------
101+
102+
task buildMockServerDockerImage {
103+
doLast {
104+
exec {
105+
workingDir "${project.projectDir}/mock-server"
106+
commandLine 'docker', 'build', '-t', 'ballerinax/apim-catalog-mock-server:latest', '.'
107+
}
108+
}
109+
}
110+
111+
task buildTokenServerDockerImage {
112+
doLast {
113+
exec {
114+
workingDir "${project.projectDir}/token-server"
115+
commandLine 'docker', 'build', '-t', 'ballerinax/apim-catalog-token-server:latest', '.'
116+
}
117+
}
118+
}
119+
120+
task buildDockerImages {
121+
dependsOn(buildMockServerDockerImage)
122+
dependsOn(buildTokenServerDockerImage)
123+
}
124+
95125
task publishTestResourcePackageToLocal {
96126
dependsOn("::${packageName}-ballerina:build")
97127
doLast {
@@ -121,6 +151,7 @@ task ballerinaTest {
121151
dependsOn(updateTomlVerions)
122152
dependsOn(initializeVariables)
123153
dependsOn(publishTestResourcePackageToLocal)
154+
dependsOn(buildDockerImages)
124155
finalizedBy(commitTomlFiles)
125156

126157
doLast {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
org = "ballerinax"
3+
name = "apim_catalog_mock_server"
4+
version = "1.0.0"
5+
distribution = "2201.13.1"
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
2+
#
3+
# WSO2 LLC. licenses this file to you under the Apache License,
4+
# Version 2.0 (the "License"); you may not use this file except
5+
# in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing,
11+
# software distributed under the License is distributed on an
12+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13+
# KIND, either express or implied. See the License for the
14+
# specific language governing permissions and limitations
15+
# under the License.
16+
17+
# Stage 1: Build the Ballerina fat JAR
18+
FROM ballerina/ballerina:2201.13.1 AS builder
19+
WORKDIR /app
20+
COPY . .
21+
RUN bal build
22+
23+
# Stage 2: Minimal runtime image
24+
FROM eclipse-temurin:21-jre
25+
WORKDIR /app
26+
COPY --from=builder /app/target/bin/apim_catalog_mock_server.jar /app/mock-server.jar
27+
28+
ENV PORT=8080
29+
ENV ARTIFACT_INDEX=0
30+
ENV UNAUTHORIZED=false
31+
ENV OUTPUT_DIR=/artifacts
32+
33+
EXPOSE ${PORT}
34+
35+
ENTRYPOINT ["java", "-jar", "/app/mock-server.jar"]
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
2+
//
3+
// WSO2 LLC. licenses this file to you under the Apache License,
4+
// Version 2.0 (the "License"); you may not use this file except
5+
// in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing,
11+
// software distributed under the License is distributed on an
12+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13+
// KIND, either express or implied. See the License for the
14+
// specific language governing permissions and limitations
15+
// under the License.
16+
17+
import ballerina/http;
18+
import ballerina/io;
19+
import ballerina/lang.runtime;
20+
import ballerina/log;
21+
import ballerina/mime;
22+
import ballerina/os;
23+
24+
type ServiceItem record {
25+
string id?;
26+
string name;
27+
string description?;
28+
string version;
29+
string serviceKey?;
30+
string serviceUrl;
31+
string definitionType;
32+
string securityType = "NONE";
33+
boolean mutualSSLEnabled = false;
34+
int usage?;
35+
string createdTime?;
36+
string lastUpdatedTime?;
37+
string md5?;
38+
string definitionUrl?;
39+
};
40+
41+
type ServiceSchema record {
42+
ServiceItem serviceMetadata;
43+
string inlineContent?;
44+
};
45+
46+
public function main() returns error? {
47+
string portStr = os:getEnv("PORT");
48+
string artifactIndexStr = os:getEnv("ARTIFACT_INDEX");
49+
string unauthorizedStr = os:getEnv("UNAUTHORIZED");
50+
string outputDir = os:getEnv("OUTPUT_DIR");
51+
52+
int port = portStr.trim() == "" ? 8080 : check int:fromString(portStr.trim());
53+
int artifactIndex = artifactIndexStr.trim() == "" ? 0 : check int:fromString(artifactIndexStr.trim());
54+
boolean unauthorized = unauthorizedStr.trim() == "true";
55+
string artifactsDir = outputDir.trim() == "" ? "/artifacts" : outputDir.trim();
56+
57+
string artifactFile = string `${artifactsDir}/artifacts_${artifactIndex}.json`;
58+
ServiceSchema[] artifacts = [];
59+
60+
http:Listener httpListener = check new (port);
61+
http:Service svc;
62+
63+
if unauthorized {
64+
svc = service object {
65+
resource function get health() returns http:Ok {
66+
return {body: {status: "ok"}};
67+
}
68+
69+
resource function get services(string? 'key = (), string? name = (), string? version = (),
70+
string? definitionType = (), boolean shrink = false, string? sortBy = (),
71+
string? sortOrder = (), int 'limit = 25, int offset = 0) returns json {
72+
return {"list": [], "pagination": {}};
73+
}
74+
75+
resource function post services(http:Request req) returns http:Unauthorized|error {
76+
ServiceSchema schema = check traverseRequest(req);
77+
artifacts.push(schema);
78+
check io:fileWriteJson(artifactFile, artifacts.toJson());
79+
return {body: {message: "Unauthorized"}};
80+
}
81+
};
82+
} else {
83+
svc = service object {
84+
resource function get health() returns http:Ok {
85+
return {body: {status: "ok"}};
86+
}
87+
88+
resource function get services(string? 'key = (), string? name = (), string? version = (),
89+
string? definitionType = (), boolean shrink = false, string? sortBy = (),
90+
string? sortOrder = (), int 'limit = 25, int offset = 0) returns json {
91+
return {"list": [], "pagination": {}};
92+
}
93+
94+
resource function post services(http:Request req) returns http:InternalServerError|error {
95+
ServiceSchema schema = check traverseRequest(req);
96+
artifacts.push(schema);
97+
check io:fileWriteJson(artifactFile, artifacts.toJson());
98+
return {body: {message: "Return 500 Status code after completing the task"}};
99+
}
100+
};
101+
}
102+
103+
check httpListener.attach(svc, "/");
104+
check httpListener.'start();
105+
runtime:registerListener(httpListener);
106+
log:printInfo(string `Mock catalog server started on port ${port}, artifactIndex=${artifactIndex}, unauthorized=${unauthorized}`);
107+
}
108+
109+
function traverseRequest(http:Request req) returns ServiceSchema|error {
110+
mime:Entity[] bodyParts = check req.getBodyParts();
111+
ServiceItem serviceMetadata = check (check bodyParts[0].getJson()).cloneWithType();
112+
string inlineContent = check bodyParts[1].getText();
113+
return {serviceMetadata, inlineContent};
114+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"Healthcare:localhost:9090/healthcare": {
3+
"serviceMetadata": {
4+
"name": "/healthcare",
5+
"description": "",
6+
"version": "0.1.0",
7+
"serviceKey": "Healthcare:localhost:9090/healthcare",
8+
"serviceUrl": "localhost:9090/healthcare",
9+
"definitionType": "OAS3",
10+
"securityType": "NONE",
11+
"mutualSSLEnabled": false,
12+
"definitionUrl": "localhost:9090/healthcare"
13+
},
14+
"inlineContent": "openapi: 3.0.1\ninfo:\n title: Healthcare\n version: 0.1.0\nservers:\n- url: \"{server}:{port}/healthcare\"\n variables:\n server:\n default: http://localhost\n port:\n default: \"9090\"\npaths:\n /querydoctor:\n post:\n operationId: postQuerydoctor\n parameters:\n - name: category\n in: query\n required: true\n schema:\n type: string\n responses:\n \"200\":\n description: Ok\n content:\n application/json:\n schema:\n type: array\n"
15+
}
16+
}

ballerina-tests/tests/generated_artifacts/.gitkeep

Whitespace-only changes.

ballerina-tests/tests/test_service_impl.bal

Lines changed: 4 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -14,149 +14,7 @@
1414
// specific language governing permissions and limitations
1515
// under the License.
1616

17-
import ballerina/http;
18-
import ballerina/io;
19-
import ballerina/log;
20-
21-
final string artifactPath = string `${ballerinaTestDir}${sep}generated_artifacts`;
22-
23-
service / on new http:Listener(8080) {
24-
ServiceSchema[] artifacts = [];
25-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_0.json`;
26-
27-
function init() returns error? {
28-
log:printInfo("Starting the test server on port 8080");
29-
}
30-
31-
resource function post services(http:Request req) returns Service|http:InternalServerError|error {
32-
return getSchemaAndReturnResponse(req, self.artifactJsonFilename, self.artifacts);
33-
}
34-
}
35-
36-
service / on new http:Listener(8081) {
37-
ServiceSchema[] artifacts = [];
38-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_1.json`;
39-
40-
function init() returns error? {
41-
log:printInfo("Starting the test server on port 8081");
42-
}
43-
44-
resource function post services(http:Request req) returns Service|http:InternalServerError|error {
45-
return getSchemaAndReturnResponse(req, self.artifactJsonFilename, self.artifacts);
46-
}
47-
}
48-
49-
service / on new http:Listener(8082) {
50-
ServiceSchema[] artifacts = [];
51-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_2.json`;
52-
53-
function init() returns error? {
54-
log:printInfo("Starting the test server on port 8082");
55-
}
56-
57-
resource function post services(http:Request req) returns Service|http:InternalServerError|error {
58-
return getSchemaAndReturnResponse(req, self.artifactJsonFilename, self.artifacts);
59-
}
60-
}
61-
62-
service / on new http:Listener(8083) {
63-
ServiceSchema[] artifacts = [];
64-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_3.json`;
65-
66-
function init() returns error? {
67-
log:printInfo("Starting the test server on port 8083");
68-
}
69-
70-
resource function post services(http:Request req) returns Service|http:InternalServerError|error {
71-
return getSchemaAndReturnResponse(req, self.artifactJsonFilename, self.artifacts);
72-
}
73-
}
74-
75-
service / on new http:Listener(8092) {
76-
ServiceSchema[] artifacts = [];
77-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_4.json`;
78-
79-
function init() returns error? {
80-
log:printInfo("Starting the test server on port 8092");
81-
}
82-
83-
resource function post services(http:Request req) returns Service|http:InternalServerError|error {
84-
return getSchemaAndReturnResponse(req, self.artifactJsonFilename, self.artifacts);
85-
}
86-
}
87-
88-
service / on new http:Listener(8085) {
89-
ServiceSchema[] artifacts = [];
90-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_5.json`;
91-
92-
function init() returns error? {
93-
log:printInfo("Starting the test server on port 8085");
94-
}
95-
96-
resource function post services(http:Request req) returns Service|http:InternalServerError|error {
97-
return getSchemaAndReturnResponse(req, self.artifactJsonFilename, self.artifacts);
98-
}
99-
}
100-
101-
service / on new http:Listener(8086) {
102-
ServiceSchema[] artifacts = [];
103-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_6.json`;
104-
105-
function init() returns error? {
106-
log:printInfo("Starting the test server on port 8086");
107-
}
108-
109-
resource function post services(http:Request req) returns Service|http:InternalServerError|error {
110-
return getSchemaAndReturnResponse(req, self.artifactJsonFilename, self.artifacts);
111-
}
112-
}
113-
114-
service / on new http:Listener(8087) {
115-
ServiceSchema[] artifacts = [];
116-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_7.json`;
117-
118-
function init() returns error? {
119-
log:printInfo("Starting the test server on port 8087");
120-
}
121-
122-
resource function post services(http:Request req) returns Service|http:InternalServerError|error {
123-
return getSchemaAndReturnResponse(req, self.artifactJsonFilename, self.artifacts);
124-
}
125-
}
126-
127-
service / on new http:Listener(8088) {
128-
ServiceSchema[] artifacts = [];
129-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_8.json`;
130-
131-
function init() returns error? {
132-
log:printInfo("Starting the test server on port 8088");
133-
}
134-
135-
resource function post services(http:Request req) returns Service|http:InternalServerError|error {
136-
return getSchemaAndReturnResponse(req, self.artifactJsonFilename, self.artifacts);
137-
}
138-
}
139-
140-
service / on new http:Listener(8091) {
141-
ServiceSchema[] artifacts = [];
142-
final string artifactJsonFilename = string `${artifactPath}${sep}artifacts_11.json`;
143-
144-
function init() returns error? {
145-
log:printInfo("Starting the test server on port 8091");
146-
}
147-
148-
resource function post services(http:Request req) returns Service|http:Unauthorized|error {
149-
ServiceSchema schema = check traverseMultiPartRequest(req);
150-
self.artifacts.push(schema);
151-
check io:fileWriteJson(self.artifactJsonFilename, self.artifacts.toJson());
152-
return {body: {message: "Unauthorized"}};
153-
}
154-
}
155-
156-
function getSchemaAndReturnResponse(http:Request req,
157-
string artifactJsonFilename, ServiceSchema[] artifacts) returns http:InternalServerError|error {
158-
ServiceSchema schema = check traverseMultiPartRequest(req);
159-
artifacts.push(schema);
160-
check io:fileWriteJson(artifactJsonFilename, artifacts.toJson());
161-
return returnDummyResponse();
162-
}
17+
// The catalog API mock server has been moved to ballerina-tests/mock-server/.
18+
// It runs as a Docker container per test group via @test:BeforeGroups/@test:AfterGroups.
19+
// See test_services.bal for the container lifecycle management and
20+
// test_utils.bal for initializeMockServerContainer()/cleanDockerContainer().

0 commit comments

Comments
 (0)