Skip to content

Commit 48c8a14

Browse files
feat: Initial implementation to run Java tests (#3)
* feat: Listen for messages in the submissions queue * feat: Implement file system manager * feat: Implement static files micro-service manager * feat: Implement initial Java tests runner * feat: Sanitize and return tests output after running them * feat: Send updates to submission status updates queue * build: Update Dockerfile
1 parent 5dd21b9 commit 48c8a14

21 files changed

+1096
-6
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
# Editor files
2-
.vscode
2+
.vscode
3+
4+
# Temp folders
5+
tmp
6+
7+
# Tests execution folder
8+
tests_exec_dir/

Dockerfile

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,23 @@ RUN go build -o /source/bin/artifact
1616
# -- Run --
1717
FROM docker.io/library/alpine:3.18 AS runner
1818

19+
# Install OpenJDK 17
20+
RUN apk --no-cache add openjdk17
21+
22+
# Install mvn
23+
RUN apk --no-cache add maven
24+
1925
# Add non-root user
20-
RUN adduser -D -h /opt/codelabs -s /sbin/nologin codelabs
26+
RUN adduser -D -h /opt/codelabs codelabs
2127
WORKDIR /opt/codelabs
2228
USER codelabs
2329

2430
# Copy binary and run
2531
COPY --from=builder /source/bin/artifact /source/bin/artifact
2632

33+
# Create tests execution directory
34+
RUN mkdir /opt/codelabs/tests-execution
35+
ENV TESTS_EXECUTION_DIRECTORY /opt/codelabs/tests-execution
36+
2737
# Run
28-
EXPOSE 8080
2938
ENTRYPOINT ["/source/bin/artifact"]

application/submissions-use-cases.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package application
2+
3+
import (
4+
"github.com/upb-code-labs/tests-microservice/domain/definitions"
5+
"github.com/upb-code-labs/tests-microservice/domain/dtos"
6+
"github.com/upb-code-labs/tests-microservice/domain/entities"
7+
"github.com/upb-code-labs/tests-microservice/infrastructure/static_files"
8+
"github.com/upb-code-labs/tests-microservice/utils"
9+
)
10+
11+
type SubmissionsUseCases struct{}
12+
13+
func (submissionsUseCases *SubmissionsUseCases) RunTests(
14+
submissionWork *entities.SubmissionWork,
15+
testsRunner definitions.LanguageTestsRunner,
16+
) dtos.TestResultDTO {
17+
// Get the archives
18+
staticFilesManager := static_files.StaticFilesManager{}
19+
20+
languageTemplateArchive, err := staticFilesManager.GetLanguageTemplateBytes(submissionWork.LanguageUUID)
21+
if err != nil {
22+
return *utils.GetTestResultDTOFromErrorMessage(
23+
submissionWork.SubmissionUUID,
24+
"[ERROR] We couldn't get the programming language archive to run the tests",
25+
)
26+
}
27+
28+
testsArchive, err := staticFilesManager.GetArchiveBytes(&dtos.GetFileFromMicroserviceDTO{
29+
FileUUID: submissionWork.TestsFileUUID,
30+
FileType: "test",
31+
})
32+
if err != nil {
33+
return *utils.GetTestResultDTOFromErrorMessage(
34+
submissionWork.SubmissionUUID,
35+
"[ERROR] We couldn't get the tests archive to run the tests",
36+
)
37+
}
38+
39+
solutionArchive, err := staticFilesManager.GetArchiveBytes(&dtos.GetFileFromMicroserviceDTO{
40+
FileUUID: submissionWork.SubmissionFileUUID,
41+
FileType: "submission",
42+
})
43+
if err != nil {
44+
return *utils.GetTestResultDTOFromErrorMessage(
45+
submissionWork.SubmissionUUID,
46+
"[ERROR] We couldn't get your submission archive to run the tests",
47+
)
48+
}
49+
50+
// Save the archives in the FS
51+
err = testsRunner.SaveArchivesInFS(&dtos.TestArchivesDTO{
52+
SubmissionUUID: submissionWork.SubmissionUUID,
53+
LanguageTemplateArchive: &languageTemplateArchive,
54+
SubmissionArchive: &solutionArchive,
55+
TestsArchive: &testsArchive,
56+
})
57+
if err != nil {
58+
return *utils.GetTestResultDTOFromErrorMessage(
59+
submissionWork.SubmissionUUID,
60+
"[ERROR] We couldn't save the archives in the file system to run the tests",
61+
)
62+
}
63+
64+
// "Merge" the archives
65+
err = testsRunner.MergeArchives(submissionWork.SubmissionUUID)
66+
if err != nil {
67+
return *utils.GetTestResultDTOFromErrorMessage(
68+
submissionWork.SubmissionUUID,
69+
"[ERROR] We couldn't prepare the archives to run the tests",
70+
)
71+
}
72+
73+
// Run the tests
74+
result, _ := testsRunner.RunTests(submissionWork.SubmissionUUID)
75+
return *result
76+
}

application/use_cases.go

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package definitions
2+
3+
type ArchivesManager interface {
4+
SaveArchiveInFS(archiveBytes *[]byte, destinationPath string) error
5+
ExtractArchive(archivePath string, destinationPath string) error
6+
MoveFilesFromDirectoryToDirectory(sourceDirectoryPath string, destinationDirectoryPath string) error
7+
DeleteArchive(archivePath string) error
8+
DeleteDirectory(directoryPath string) error
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package definitions
2+
3+
import (
4+
"github.com/upb-code-labs/tests-microservice/domain/dtos"
5+
)
6+
7+
type LanguageTestsRunner interface {
8+
RunTests(submissionUUID string) (*dtos.TestResultDTO, error)
9+
SaveArchivesInFS(dto *dtos.TestArchivesDTO) error
10+
MergeArchives(submissionUUID string) error
11+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package definitions
2+
3+
import "github.com/upb-code-labs/tests-microservice/domain/dtos"
4+
5+
type SubmissionStatusUpdatesQueueManager interface {
6+
QueueUpdate(updateDTO *dtos.SubmissionStatusUpdateDTO) error
7+
}

domain/dtos/dtos.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package dtos
2+
3+
import (
4+
"regexp"
5+
)
6+
7+
type GetFileFromMicroserviceDTO struct {
8+
FileUUID string `json:"archive_uuid"`
9+
FileType string `json:"archive_type"`
10+
}
11+
12+
type TestArchivesDTO struct {
13+
SubmissionUUID string
14+
LanguageTemplateArchive *[]byte
15+
SubmissionArchive *[]byte
16+
TestsArchive *[]byte
17+
}
18+
19+
type ReplaceRegexDTO struct {
20+
Regexp regexp.Regexp
21+
Replacement string
22+
}
23+
24+
type TestResultDTO struct {
25+
SubmissionUUID string `json:"submission_uuid"`
26+
TestsPassed bool `json:"tests_passed"`
27+
TestsOutput string `json:"tests_output"`
28+
}
29+
30+
func (dto *TestResultDTO) ToSubmissionStatusUpdateDTO(status string) *SubmissionStatusUpdateDTO {
31+
return &SubmissionStatusUpdateDTO{
32+
SubmissionUUID: dto.SubmissionUUID,
33+
SubmissionStatus: status,
34+
TestsPassed: dto.TestsPassed,
35+
TestsOutput: dto.TestsOutput,
36+
}
37+
}
38+
39+
type SubmissionStatusUpdateDTO struct {
40+
SubmissionUUID string `json:"submission_uuid"`
41+
SubmissionStatus string `json:"submission_status"`
42+
TestsPassed bool `json:"tests_passed"`
43+
TestsOutput string `json:"tests_output"`
44+
}

domain/entities/entities.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
11
package entities
2+
3+
type SubmissionWork struct {
4+
SubmissionUUID string `json:"submission_uuid"`
5+
LanguageUUID string `json:"language_uuid"`
6+
SubmissionFileUUID string `json:"submission_archive_uuid"`
7+
TestsFileUUID string `json:"test_archive_uuid"`
8+
}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
module github.com/upb-code-labs/tests-microservice
22

33
go 1.21.5
4+
5+
require (
6+
github.com/kelseyhightower/envconfig v1.4.0
7+
github.com/rabbitmq/amqp091-go v1.9.0
8+
)

go.sum

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
4+
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
5+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
6+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
7+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
8+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9+
github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo=
10+
github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
11+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
12+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
13+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
14+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
15+
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
16+
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
17+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
18+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
19+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
20+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

infrastructure/environment.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package infrastructure
2+
3+
import "github.com/kelseyhightower/envconfig"
4+
5+
type Environment struct {
6+
RabbitMQConnectionString string `split_words:"true" default:"amqp://rabbitmq:rabbitmq@localhost:5672/"`
7+
StaticFilesMicroserviceAddress string `split_words:"true" default:"http://localhost:8081"`
8+
TestsExecutionDirectory string `split_words:"true" default:"./tests_exec_dir"`
9+
}
10+
11+
var env *Environment
12+
13+
func GetEnvironment() *Environment {
14+
if env == nil {
15+
env = &Environment{}
16+
17+
err := envconfig.Process("", env)
18+
if err != nil {
19+
panic(err)
20+
}
21+
}
22+
23+
return env
24+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package implementations
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"os/exec"
8+
"time"
9+
)
10+
11+
type ArchivesManagerImplementation struct{}
12+
13+
func (archivesManagerImplementation *ArchivesManagerImplementation) SaveArchiveInFS(archiveBytes *[]byte, destinationPath string) error {
14+
// Create the file
15+
file, err := os.Create(destinationPath)
16+
if err != nil {
17+
return err
18+
}
19+
20+
defer file.Close()
21+
22+
// Write the file
23+
_, err = file.Write(*archiveBytes)
24+
if err != nil {
25+
return err
26+
}
27+
28+
// Reset the file pointer
29+
_, err = file.Seek(0, 0)
30+
if err != nil {
31+
return err
32+
}
33+
34+
// Save the file
35+
err = file.Sync()
36+
if err != nil {
37+
return err
38+
}
39+
40+
return nil
41+
}
42+
43+
func (archivesManagerImplementation *ArchivesManagerImplementation) ExtractArchive(archivePath string, destinationPath string) error {
44+
context, cancel := context.WithTimeout(context.Background(), 1*time.Second)
45+
defer cancel()
46+
47+
cmd := exec.CommandContext(
48+
context,
49+
"unzip",
50+
archivePath,
51+
"-d",
52+
destinationPath,
53+
)
54+
55+
err := cmd.Run()
56+
if err != nil {
57+
return err
58+
}
59+
60+
return nil
61+
}
62+
63+
func (archivesManagerImplementation *ArchivesManagerImplementation) MoveFilesFromDirectoryToDirectory(sourceDirectoryPath string, destinationDirectoryPath string) error {
64+
context, cancel := context.WithTimeout(context.Background(), 1*time.Second)
65+
defer cancel()
66+
67+
moveCommand := fmt.Sprintf(
68+
"mv %s %s",
69+
sourceDirectoryPath,
70+
destinationDirectoryPath,
71+
)
72+
73+
cmd := exec.CommandContext(
74+
context,
75+
"sh",
76+
"-c",
77+
moveCommand,
78+
)
79+
80+
err := cmd.Run()
81+
if err != nil {
82+
return err
83+
}
84+
85+
return nil
86+
}
87+
88+
func (archivesManagerImplementation *ArchivesManagerImplementation) DeleteArchive(archivePath string) error {
89+
err := os.Remove(archivePath)
90+
if err != nil {
91+
return err
92+
}
93+
94+
return nil
95+
}
96+
97+
func (archivesManagerImplementation *ArchivesManagerImplementation) DeleteDirectory(directoryPath string) error {
98+
err := os.RemoveAll(directoryPath)
99+
if err != nil {
100+
return err
101+
}
102+
103+
return nil
104+
}

0 commit comments

Comments
 (0)