Skip to content
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
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
# Editor files
.vscode
.vscode

# Temp folders
tmp

# Tests execution folder
tests_exec_dir/
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 0.1.0 (2024-01-05)


### Features

* Initial implementation to run `Java` tests ([#3](https://github.com/upb-code-labs/tests-microservice/issues/3)) ([48c8a14](https://github.com/upb-code-labs/tests-microservice/commit/48c8a14aea266e3a7748009bb5ba72e6043f2be7))



13 changes: 11 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,23 @@ RUN go build -o /source/bin/artifact
# -- Run --
FROM docker.io/library/alpine:3.18 AS runner

# Install OpenJDK 17
RUN apk --no-cache add openjdk17

# Install mvn
RUN apk --no-cache add maven

# Add non-root user
RUN adduser -D -h /opt/codelabs -s /sbin/nologin codelabs
RUN adduser -D -h /opt/codelabs codelabs
WORKDIR /opt/codelabs
USER codelabs

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

# Create tests execution directory
RUN mkdir /opt/codelabs/tests-execution
ENV TESTS_EXECUTION_DIRECTORY /opt/codelabs/tests-execution

# Run
EXPOSE 8080
ENTRYPOINT ["/source/bin/artifact"]
76 changes: 76 additions & 0 deletions application/submissions-use-cases.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package application

import (
"github.com/upb-code-labs/tests-microservice/domain/definitions"
"github.com/upb-code-labs/tests-microservice/domain/dtos"
"github.com/upb-code-labs/tests-microservice/domain/entities"
"github.com/upb-code-labs/tests-microservice/infrastructure/static_files"
"github.com/upb-code-labs/tests-microservice/utils"
)

type SubmissionsUseCases struct{}

func (submissionsUseCases *SubmissionsUseCases) RunTests(
submissionWork *entities.SubmissionWork,
testsRunner definitions.LanguageTestsRunner,
) dtos.TestResultDTO {
// Get the archives
staticFilesManager := static_files.StaticFilesManager{}

languageTemplateArchive, err := staticFilesManager.GetLanguageTemplateBytes(submissionWork.LanguageUUID)
if err != nil {
return *utils.GetTestResultDTOFromErrorMessage(
submissionWork.SubmissionUUID,
"[ERROR] We couldn't get the programming language archive to run the tests",
)
}

testsArchive, err := staticFilesManager.GetArchiveBytes(&dtos.GetFileFromMicroserviceDTO{
FileUUID: submissionWork.TestsFileUUID,
FileType: "test",
})
if err != nil {
return *utils.GetTestResultDTOFromErrorMessage(
submissionWork.SubmissionUUID,
"[ERROR] We couldn't get the tests archive to run the tests",
)
}

solutionArchive, err := staticFilesManager.GetArchiveBytes(&dtos.GetFileFromMicroserviceDTO{
FileUUID: submissionWork.SubmissionFileUUID,
FileType: "submission",
})
if err != nil {
return *utils.GetTestResultDTOFromErrorMessage(
submissionWork.SubmissionUUID,
"[ERROR] We couldn't get your submission archive to run the tests",
)
}

// Save the archives in the FS
err = testsRunner.SaveArchivesInFS(&dtos.TestArchivesDTO{
SubmissionUUID: submissionWork.SubmissionUUID,
LanguageTemplateArchive: &languageTemplateArchive,
SubmissionArchive: &solutionArchive,
TestsArchive: &testsArchive,
})
if err != nil {
return *utils.GetTestResultDTOFromErrorMessage(
submissionWork.SubmissionUUID,
"[ERROR] We couldn't save the archives in the file system to run the tests",
)
}

// "Merge" the archives
err = testsRunner.MergeArchives(submissionWork.SubmissionUUID)
if err != nil {
return *utils.GetTestResultDTOFromErrorMessage(
submissionWork.SubmissionUUID,
"[ERROR] We couldn't prepare the archives to run the tests",
)
}

// Run the tests
result, _ := testsRunner.RunTests(submissionWork.SubmissionUUID)
return *result
}
1 change: 0 additions & 1 deletion application/use_cases.go

This file was deleted.

9 changes: 9 additions & 0 deletions domain/definitions/archives_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package definitions

type ArchivesManager interface {
SaveArchiveInFS(archiveBytes *[]byte, destinationPath string) error
ExtractArchive(archivePath string, destinationPath string) error
MoveFilesFromDirectoryToDirectory(sourceDirectoryPath string, destinationDirectoryPath string) error
DeleteArchive(archivePath string) error
DeleteDirectory(directoryPath string) error
}
11 changes: 11 additions & 0 deletions domain/definitions/language-tests-runner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package definitions

import (
"github.com/upb-code-labs/tests-microservice/domain/dtos"
)

type LanguageTestsRunner interface {
RunTests(submissionUUID string) (*dtos.TestResultDTO, error)
SaveArchivesInFS(dto *dtos.TestArchivesDTO) error
MergeArchives(submissionUUID string) error
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package definitions

import "github.com/upb-code-labs/tests-microservice/domain/dtos"

type SubmissionStatusUpdatesQueueManager interface {
QueueUpdate(updateDTO *dtos.SubmissionStatusUpdateDTO) error
}
44 changes: 44 additions & 0 deletions domain/dtos/dtos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package dtos

import (
"regexp"
)

type GetFileFromMicroserviceDTO struct {
FileUUID string `json:"archive_uuid"`
FileType string `json:"archive_type"`
}

type TestArchivesDTO struct {
SubmissionUUID string
LanguageTemplateArchive *[]byte
SubmissionArchive *[]byte
TestsArchive *[]byte
}

type ReplaceRegexDTO struct {
Regexp regexp.Regexp
Replacement string
}

type TestResultDTO struct {
SubmissionUUID string `json:"submission_uuid"`
TestsPassed bool `json:"tests_passed"`
TestsOutput string `json:"tests_output"`
}

func (dto *TestResultDTO) ToSubmissionStatusUpdateDTO(status string) *SubmissionStatusUpdateDTO {
return &SubmissionStatusUpdateDTO{
SubmissionUUID: dto.SubmissionUUID,
SubmissionStatus: status,
TestsPassed: dto.TestsPassed,
TestsOutput: dto.TestsOutput,
}
}

type SubmissionStatusUpdateDTO struct {
SubmissionUUID string `json:"submission_uuid"`
SubmissionStatus string `json:"submission_status"`
TestsPassed bool `json:"tests_passed"`
TestsOutput string `json:"tests_output"`
}
7 changes: 7 additions & 0 deletions domain/entities/entities.go
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
package entities

type SubmissionWork struct {
SubmissionUUID string `json:"submission_uuid"`
LanguageUUID string `json:"language_uuid"`
SubmissionFileUUID string `json:"submission_archive_uuid"`
TestsFileUUID string `json:"test_archive_uuid"`
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
module github.com/upb-code-labs/tests-microservice

go 1.21.5

require (
github.com/kelseyhightower/envconfig v1.4.0
github.com/rabbitmq/amqp091-go v1.9.0
)
20 changes: 20 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo=
github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
24 changes: 24 additions & 0 deletions infrastructure/environment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package infrastructure

import "github.com/kelseyhightower/envconfig"

type Environment struct {
RabbitMQConnectionString string `split_words:"true" default:"amqp://rabbitmq:rabbitmq@localhost:5672/"`
StaticFilesMicroserviceAddress string `split_words:"true" default:"http://localhost:8081"`
TestsExecutionDirectory string `split_words:"true" default:"./tests_exec_dir"`
}

var env *Environment

func GetEnvironment() *Environment {
if env == nil {
env = &Environment{}

err := envconfig.Process("", env)
if err != nil {
panic(err)
}
}

return env
}
104 changes: 104 additions & 0 deletions infrastructure/implementations/archives_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package implementations

import (
"context"
"fmt"
"os"
"os/exec"
"time"
)

type ArchivesManagerImplementation struct{}

func (archivesManagerImplementation *ArchivesManagerImplementation) SaveArchiveInFS(archiveBytes *[]byte, destinationPath string) error {
// Create the file
file, err := os.Create(destinationPath)
if err != nil {
return err
}

defer file.Close()

// Write the file
_, err = file.Write(*archiveBytes)
if err != nil {
return err
}

// Reset the file pointer
_, err = file.Seek(0, 0)
if err != nil {
return err
}

// Save the file
err = file.Sync()
if err != nil {
return err
}

return nil
}

func (archivesManagerImplementation *ArchivesManagerImplementation) ExtractArchive(archivePath string, destinationPath string) error {
context, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()

cmd := exec.CommandContext(
context,
"unzip",
archivePath,
"-d",
destinationPath,
)

err := cmd.Run()
if err != nil {
return err
}

return nil
}

func (archivesManagerImplementation *ArchivesManagerImplementation) MoveFilesFromDirectoryToDirectory(sourceDirectoryPath string, destinationDirectoryPath string) error {
context, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()

moveCommand := fmt.Sprintf(
"mv %s %s",
sourceDirectoryPath,
destinationDirectoryPath,
)

cmd := exec.CommandContext(
context,
"sh",
"-c",
moveCommand,
)

err := cmd.Run()
if err != nil {
return err
}

return nil
}

func (archivesManagerImplementation *ArchivesManagerImplementation) DeleteArchive(archivePath string) error {
err := os.Remove(archivePath)
if err != nil {
return err
}

return nil
}

func (archivesManagerImplementation *ArchivesManagerImplementation) DeleteDirectory(directoryPath string) error {
err := os.RemoveAll(directoryPath)
if err != nil {
return err
}

return nil
}
Loading