added k8s stub adapter for execution environment

This commit is contained in:
Elmar Kresse
2024-09-18 10:43:38 +02:00
parent f9a6ba8f1c
commit 12ff205bd2
119 changed files with 1374 additions and 12549 deletions

View File

@@ -1,83 +0,0 @@
package ws
import (
"context"
"github.com/gorilla/websocket"
"github.com/openHPI/poseidon/tests"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"io"
"strings"
"testing"
)
type MainTestSuite struct {
tests.MemoryLeakTestSuite
}
func TestMainTestSuite(t *testing.T) {
suite.Run(t, new(MainTestSuite))
}
func (s *MainTestSuite) TestCodeOceanToRawReaderReturnsOnlyAfterOneByteWasRead() {
readingCtx, cancel := context.WithCancel(context.Background())
forwardingCtx := readingCtx
defer cancel()
reader := NewCodeOceanToRawReader(nil, readingCtx, forwardingCtx)
read := make(chan bool)
go func() {
//nolint:makezero // we can't make zero initial length here as the reader otherwise doesn't block
p := make([]byte, 10)
_, err := reader.Read(p)
s.Require().NoError(err)
read <- true
}()
s.Run("Does not return immediately when there is no data", func() {
s.False(tests.ChannelReceivesSomething(read, tests.ShortTimeout))
})
s.Run("Returns when there is data available", func() {
reader.buffer <- byte(42)
s.True(tests.ChannelReceivesSomething(read, tests.ShortTimeout))
})
}
func (s *MainTestSuite) TestCodeOceanToRawReaderReturnsOnlyAfterOneByteWasReadFromConnection() {
messages := make(chan io.Reader)
defer close(messages)
connection := &ConnectionMock{}
connection.On("WriteMessage", mock.AnythingOfType("int"), mock.AnythingOfType("[]uint8")).Return(nil)
connection.On("CloseHandler").Return(nil)
connection.On("SetCloseHandler", mock.Anything).Return()
call := connection.On("NextReader")
call.Run(func(_ mock.Arguments) {
call.Return(websocket.TextMessage, <-messages, nil)
})
readingCtx, cancel := context.WithCancel(context.Background())
forwardingCtx := readingCtx
defer cancel()
reader := NewCodeOceanToRawReader(connection, readingCtx, forwardingCtx)
reader.Start()
read := make(chan bool)
//nolint:makezero // this is required here to make the Read call blocking
message := make([]byte, 10)
go func() {
_, err := reader.Read(message)
s.Require().NoError(err)
read <- true
}()
s.Run("Does not return immediately when there is no data", func() {
s.False(tests.ChannelReceivesSomething(read, tests.ShortTimeout))
})
s.Run("Returns when there is data available", func() {
messages <- strings.NewReader("Hello")
s.True(tests.ChannelReceivesSomething(read, tests.ShortTimeout))
})
}

View File

@@ -1,107 +0,0 @@
package ws
import (
"context"
"encoding/json"
"github.com/gorilla/websocket"
"github.com/openHPI/poseidon/internal/runner"
"github.com/openHPI/poseidon/pkg/dto"
"github.com/openHPI/poseidon/tests"
"github.com/stretchr/testify/mock"
)
func (s *MainTestSuite) TestRawToCodeOceanWriter() {
connectionMock, messages := buildConnectionMock(&s.MemoryLeakTestSuite)
proxyCtx, cancel := context.WithCancel(context.Background())
defer cancel()
output := NewCodeOceanOutputWriter(connectionMock, proxyCtx, cancel)
defer output.Close(nil)
<-messages // start messages
s.Run("StdOut", func() {
testMessage := "testStdOut"
_, err := output.StdOut().Write([]byte(testMessage))
s.Require().NoError(err)
expected, err := json.Marshal(struct {
Type string `json:"type"`
Data string `json:"data"`
}{string(dto.WebSocketOutputStdout), testMessage})
s.Require().NoError(err)
s.Equal(expected, <-messages)
})
s.Run("StdErr", func() {
testMessage := "testStdErr"
_, err := output.StdErr().Write([]byte(testMessage))
s.Require().NoError(err)
expected, err := json.Marshal(struct {
Type string `json:"type"`
Data string `json:"data"`
}{string(dto.WebSocketOutputStderr), testMessage})
s.Require().NoError(err)
s.Equal(expected, <-messages)
})
}
type sendExitInfoTestCase struct {
name string
info *runner.ExitInfo
message dto.WebSocketMessage
}
func (s *MainTestSuite) TestCodeOceanOutputWriter_SendExitInfo() {
testCases := []sendExitInfoTestCase{
{"Timeout", &runner.ExitInfo{Err: runner.ErrorRunnerInactivityTimeout},
dto.WebSocketMessage{Type: dto.WebSocketMetaTimeout}},
{"Error", &runner.ExitInfo{Err: websocket.ErrCloseSent},
dto.WebSocketMessage{Type: dto.WebSocketOutputError, Data: "Error executing the request"}},
// CodeOcean expects this exact string in case of a OOM Killed runner.
{"Specific data for OOM Killed runner", &runner.ExitInfo{Err: runner.ErrOOMKilled},
dto.WebSocketMessage{Type: dto.WebSocketOutputError, Data: "the allocation was OOM Killed"}},
{"Exit", &runner.ExitInfo{Code: 21},
dto.WebSocketMessage{Type: dto.WebSocketExit, ExitCode: 21}},
}
for _, test := range testCases {
s.Run(test.name, func() {
connectionMock, messages := buildConnectionMock(&s.MemoryLeakTestSuite)
proxyCtx, cancel := context.WithCancel(context.Background())
defer cancel()
output := NewCodeOceanOutputWriter(connectionMock, proxyCtx, cancel)
<-messages // start messages
output.Close(test.info)
expected, err := json.Marshal(test.message)
s.Require().NoError(err)
msg := <-messages
s.Equal(expected, msg)
<-messages // close message
})
}
}
func buildConnectionMock(s *tests.MemoryLeakTestSuite) (conn *ConnectionMock, messages <-chan []byte) {
s.T().Helper()
message := make(chan []byte)
connectionMock := &ConnectionMock{}
connectionMock.On("WriteMessage", mock.AnythingOfType("int"), mock.AnythingOfType("[]uint8")).
Run(func(args mock.Arguments) {
m, ok := args.Get(1).([]byte)
s.Require().True(ok)
select {
case <-s.TestCtx.Done():
case message <- m:
}
}).
Return(nil)
connectionMock.On("CloseHandler").Return(nil)
connectionMock.On("SetCloseHandler", mock.Anything).Return()
connectionMock.On("Close").Return(nil)
return connectionMock, message
}

View File

@@ -1,108 +0,0 @@
// Code generated by mockery v2.13.1. DO NOT EDIT.
package ws
import (
io "io"
mock "github.com/stretchr/testify/mock"
)
// ConnectionMock is an autogenerated mock type for the Connection type
type ConnectionMock struct {
mock.Mock
}
// Close provides a mock function with given fields:
func (_m *ConnectionMock) Close() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// CloseHandler provides a mock function with given fields:
func (_m *ConnectionMock) CloseHandler() func(int, string) error {
ret := _m.Called()
var r0 func(int, string) error
if rf, ok := ret.Get(0).(func() func(int, string) error); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(func(int, string) error)
}
}
return r0
}
// NextReader provides a mock function with given fields:
func (_m *ConnectionMock) NextReader() (int, io.Reader, error) {
ret := _m.Called()
var r0 int
if rf, ok := ret.Get(0).(func() int); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(int)
}
var r1 io.Reader
if rf, ok := ret.Get(1).(func() io.Reader); ok {
r1 = rf()
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(io.Reader)
}
}
var r2 error
if rf, ok := ret.Get(2).(func() error); ok {
r2 = rf()
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// SetCloseHandler provides a mock function with given fields: handler
func (_m *ConnectionMock) SetCloseHandler(handler func(int, string) error) {
_m.Called(handler)
}
// WriteMessage provides a mock function with given fields: messageType, data
func (_m *ConnectionMock) WriteMessage(messageType int, data []byte) error {
ret := _m.Called(messageType, data)
var r0 error
if rf, ok := ret.Get(0).(func(int, []byte) error); ok {
r0 = rf(messageType, data)
} else {
r0 = ret.Error(0)
}
return r0
}
type mockConstructorTestingTNewConnectionMock interface {
mock.TestingT
Cleanup(func())
}
// NewConnectionMock creates a new instance of ConnectionMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewConnectionMock(t mockConstructorTestingTNewConnectionMock) *ConnectionMock {
mock := &ConnectionMock{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}