mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-07-16 17:48:49 +02:00
Merge branch 'development' into 'main'
Development See merge request htwk-software/htwkalender!47
This commit is contained in:
@ -26,6 +26,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- pb_data:/htwkalender-data-manager/data # for production with volume
|
- pb_data:/htwkalender-data-manager/data # for production with volume
|
||||||
# - ./data-manager:/htwkalender/data # for development with bind mount from project directory
|
# - ./data-manager:/htwkalender/data # for development with bind mount from project directory
|
||||||
|
user: "ical"
|
||||||
|
|
||||||
htwkalender-ical:
|
htwkalender-ical:
|
||||||
build:
|
build:
|
||||||
|
@ -29,19 +29,20 @@ COPY common/. ./common
|
|||||||
RUN CGO_ENABLED=1 GOOS=linux go build -o /htwkalender-data-manager data-manager/main.go
|
RUN CGO_ENABLED=1 GOOS=linux go build -o /htwkalender-data-manager data-manager/main.go
|
||||||
|
|
||||||
# production stage
|
# production stage
|
||||||
FROM alpine:latest AS prod
|
FROM alpine:3.20.1 AS prod
|
||||||
|
|
||||||
WORKDIR /htwkalender-data-manager
|
WORKDIR /htwkalender-data-manager
|
||||||
|
|
||||||
ARG USER=ical
|
ARG USER=ical
|
||||||
RUN adduser -Ds /bin/sh $USER && \
|
RUN adduser -Ds /bin/sh "$USER" && \
|
||||||
chown $USER:$USER ./
|
chown "$USER":"$USER" ./
|
||||||
|
|
||||||
USER $USER
|
USER $USER
|
||||||
RUN mkdir -p data
|
RUN mkdir -p data
|
||||||
|
|
||||||
# copies executable from build container
|
# copies executable from build container
|
||||||
COPY --chown=$USER:$USER --from=build /htwkalender-data-manager ./
|
COPY --chown=$USER:$USER --chmod=644 --from=build /htwkalender-data-manager ./
|
||||||
|
RUN chmod +x main
|
||||||
|
|
||||||
# Expose port 8090 to the outside world
|
# Expose port 8090 to the outside world
|
||||||
EXPOSE 8090
|
EXPOSE 8090
|
||||||
@ -54,6 +55,12 @@ FROM golang:1.21.6 AS dev
|
|||||||
# Set the Current Working Directory inside the container
|
# Set the Current Working Directory inside the container
|
||||||
WORKDIR /htwkalender-data-manager
|
WORKDIR /htwkalender-data-manager
|
||||||
|
|
||||||
|
ARG USER=ical
|
||||||
|
RUN adduser "$USER" && \
|
||||||
|
chown "$USER":"$USER" ./ \
|
||||||
|
&& mkdir -p /htwkalender-data-manager/data \
|
||||||
|
&& chown "$USER":"$USER" /htwkalender-data-manager/data
|
||||||
|
|
||||||
# Copy go mod and sum files
|
# Copy go mod and sum files
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
@ -68,5 +75,7 @@ RUN CGO_ENABLED=1 GOOS=linux go build -o /htwkalender-data-manager data-manager/
|
|||||||
# Expose port 8091 to the outside world
|
# Expose port 8091 to the outside world
|
||||||
EXPOSE 8091
|
EXPOSE 8091
|
||||||
|
|
||||||
|
USER $USER
|
||||||
|
|
||||||
# Entry point
|
# Entry point
|
||||||
ENTRYPOINT ["./main", "serve"]
|
ENTRYPOINT ["./main", "serve"]
|
@ -27,7 +27,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func setupApp() *pocketbase.PocketBase {
|
||||||
app := pocketbase.New()
|
app := pocketbase.New()
|
||||||
|
|
||||||
// loosely check if it was executed using "go run"
|
// loosely check if it was executed using "go run"
|
||||||
@ -44,8 +44,12 @@ func main() {
|
|||||||
service.AddRoutes(app)
|
service.AddRoutes(app)
|
||||||
service.AddSchedules(app)
|
service.AddSchedules(app)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := setupApp()
|
||||||
if err := app.Start(); err != nil {
|
if err := app.Start(); err != nil {
|
||||||
slog.Error("Failed to start app: ", "error", err)
|
slog.Error("Failed to start app: ", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
42
services/data-manager/main_test.go
Normal file
42
services/data-manager/main_test.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||||
|
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||||
|
|
||||||
|
//This program is free software: you can redistribute it and/or modify
|
||||||
|
//it under the terms of the GNU Affero General Public License as published by
|
||||||
|
//the Free Software Foundation, either version 3 of the License, or
|
||||||
|
//(at your option) any later version.
|
||||||
|
|
||||||
|
//This program is distributed in the hope that it will be useful,
|
||||||
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
//GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
//You should have received a copy of the GNU Affero General Public License
|
||||||
|
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetupApp(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Test setupApp",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
app := setupApp()
|
||||||
|
go func() {
|
||||||
|
if err := app.Start(); err != nil {
|
||||||
|
t.Errorf("Failed to start app: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ import (
|
|||||||
"github.com/pocketbase/pocketbase/tools/types"
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEvents_Contains(t *testing.T) {
|
func TestEventsContains(t *testing.T) {
|
||||||
specificTime, _ := types.ParseDateTime("2020-01-01 12:00:00.000Z")
|
specificTime, _ := types.ParseDateTime("2020-01-01 12:00:00.000Z")
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
@ -70,7 +70,7 @@ func TestEvents_Contains(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvent_Equals(t *testing.T) {
|
func TestEventEquals(t *testing.T) {
|
||||||
specificTime, _ := types.ParseDateTime("2020-01-01 12:00:00.000Z")
|
specificTime, _ := types.ParseDateTime("2020-01-01 12:00:00.000Z")
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
@ -148,7 +148,7 @@ func TestEvent_Equals(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvent_AnonymizeEvent(t *testing.T) {
|
func TestEventAnonymizeEvent(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
UUID string
|
UUID string
|
||||||
Day string
|
Day string
|
||||||
@ -218,7 +218,7 @@ func TestEvent_AnonymizeEvent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvent_GetName(t *testing.T) {
|
func TestEventGetName(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
UUID string
|
UUID string
|
||||||
Day string
|
Day string
|
||||||
@ -278,7 +278,7 @@ func TestEvent_GetName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvent_SetCourse(t *testing.T) {
|
func TestEventSetCourse(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
UUID string
|
UUID string
|
||||||
Day string
|
Day string
|
||||||
@ -338,7 +338,7 @@ func TestEvent_SetCourse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvent_SetName(t *testing.T) {
|
func TestEventSetName(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
UUID string
|
UUID string
|
||||||
Day string
|
Day string
|
||||||
@ -398,7 +398,7 @@ func TestEvent_SetName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvent_TableName(t *testing.T) {
|
func TestEventTableName(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
UUID string
|
UUID string
|
||||||
Day string
|
Day string
|
||||||
@ -453,7 +453,7 @@ func TestEvent_TableName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvents_Contains1(t *testing.T) {
|
func TestEventsContains1(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
event Event
|
event Event
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
_ "time/tzdata"
|
_ "time/tzdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_getDateFromWeekNumber(t *testing.T) {
|
func TestGetDateFromWeekNumber(t *testing.T) {
|
||||||
europeTime, _ := time.LoadLocation("Europe/Berlin")
|
europeTime, _ := time.LoadLocation("Europe/Berlin")
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_buildIcalQueryForModules(t *testing.T) {
|
func TestBuildIcalQueryForModules(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
modules []string
|
modules []string
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_removeEmptyCourses(t *testing.T) {
|
func TestRemoveEmptyCourses(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
courses []string
|
courses []string
|
||||||
}
|
}
|
||||||
|
@ -122,10 +122,12 @@ func DeleteAllEvents(app *pocketbase.PocketBase) error {
|
|||||||
// If the update was not successful, an error is returned
|
// If the update was not successful, an error is returned
|
||||||
func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) (model.Events, error) {
|
func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) (model.Events, error) {
|
||||||
|
|
||||||
seminarGroup := v1.GetSeminarGroupEventsFromHTML(course)
|
seminarGroup, err := v1.GetSeminarGroupEventsFromHTML(course)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
seminarGroup = v1.ClearEmptySeminarGroups(seminarGroup)
|
seminarGroup = v1.ClearEmptySeminarGroups(seminarGroup)
|
||||||
|
|
||||||
seminarGroup = v1.ReplaceEmptyEventNames(seminarGroup)
|
seminarGroup = v1.ReplaceEmptyEventNames(seminarGroup)
|
||||||
|
|
||||||
//check if events in the seminarGroups Events are already in the database
|
//check if events in the seminarGroups Events are already in the database
|
||||||
|
@ -19,6 +19,7 @@ package fetch
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -26,18 +27,27 @@ import (
|
|||||||
// getPlanHTML Get the HTML document from the specified URL
|
// getPlanHTML Get the HTML document from the specified URL
|
||||||
|
|
||||||
func GetHTML(url string) (string, error) {
|
func GetHTML(url string) (string, error) {
|
||||||
|
|
||||||
// Create HTTP client with timeout of 5 seconds
|
// Create HTTP client with timeout of 5 seconds
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
}
|
}
|
||||||
|
return GetHTMLWithClient(url, &client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHTMLWithClient(url string, client *http.Client) (string, error) {
|
||||||
|
|
||||||
// Send GET request
|
// Send GET request
|
||||||
response, err := client.Get(url)
|
response, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error occurred while making the request: %s\n", err.Error())
|
slog.Error("Error occurred while fetching the HTML document:", "error", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if response.StatusCode != 200 {
|
||||||
|
slog.Warn("While fetching the HTML document, the server responded with status code: ", "status", response.StatusCode)
|
||||||
|
return "", fmt.Errorf("server responded with status code: %d", response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
defer func(Body io.ReadCloser) {
|
defer func(Body io.ReadCloser) {
|
||||||
err := Body.Close()
|
err := Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
63
services/data-manager/service/fetch/htmlDownloader_test.go
Normal file
63
services/data-manager/service/fetch/htmlDownloader_test.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package fetch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jarcoal/httpmock"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetHTMLWithClient(t *testing.T) {
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
httpmock.ActivateNonDefault(client)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
url string
|
||||||
|
statusCode int
|
||||||
|
method string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Test GetHTML with status code 200",
|
||||||
|
args: args{
|
||||||
|
url: "https://stundenplan.htwk-leipzig.de/ss/Berichte/Text-Listen;Studenten-Sets;name;1-1?template=sws_semgrp&weeks=1-65",
|
||||||
|
method: "GET",
|
||||||
|
statusCode: 200,
|
||||||
|
},
|
||||||
|
want: "",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test GetHTML with status code 404",
|
||||||
|
args: args{
|
||||||
|
url: "https://stundenplan.htwk-leipzig.de/ss/Berichte/Text-Lists;Studenten-Sets;name;1-1?template=sws_semgrp&weeks=1-65",
|
||||||
|
method: "GET",
|
||||||
|
statusCode: 404,
|
||||||
|
},
|
||||||
|
want: "",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
httpmock.RegisterResponder(tt.args.method, tt.args.url,
|
||||||
|
httpmock.NewStringResponder(tt.args.statusCode, tt.want))
|
||||||
|
got, err := GetHTMLWithClient(tt.args.url, client)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("GetHTML() error = %v, wantNoErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("GetHTML() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
httpmock.DeactivateAndReset()
|
||||||
|
}
|
@ -21,7 +21,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_splitByCommaWithTime(t *testing.T) {
|
func TestSplitByCommaWithTime(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
input string
|
input string
|
||||||
}
|
}
|
||||||
|
@ -50,25 +50,60 @@ func ClearEmptySeminarGroups(seminarGroup model.SeminarGroup) model.SeminarGroup
|
|||||||
return newSeminarGroup
|
return newSeminarGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSeminarGroupEventsFromHTML(seminarGroupLabel string) model.SeminarGroup {
|
func fetchHTMLFromURL(semester, seminarGroupLabel string) (string, error) {
|
||||||
var seminarGroup model.SeminarGroup
|
url := "https://stundenplan.htwk-leipzig.de/" + semester + "/Berichte/Text-Listen;Studenten-Sets;name;" + seminarGroupLabel + "?template=sws_semgrp&weeks=1-65"
|
||||||
|
result, err := fetch.GetHTML(url)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Error occurred while fetching the HTML document:", "error", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
if (time.Now().Month() >= 3) && (time.Now().Month() <= 10) {
|
func GetSeminarGroupEventsFromHTML(seminarGroupLabel string) (model.SeminarGroup, error) {
|
||||||
ssUrl := "https://stundenplan.htwk-leipzig.de/" + string("ss") + "/Berichte/Text-Listen;Studenten-Sets;name;" + seminarGroupLabel + "?template=sws_semgrp&weeks=1-65"
|
var seminarGroup [2]model.SeminarGroup
|
||||||
result, getError := fetch.GetHTML(ssUrl)
|
var errSS, errWS error
|
||||||
if getError == nil {
|
|
||||||
seminarGroup = parseSeminarGroup(result)
|
currentMonth := time.Now().Month()
|
||||||
}
|
|
||||||
|
if isSummerSemester(currentMonth) {
|
||||||
|
seminarGroup[0], errSS = fetchAndParse("ss", seminarGroupLabel)
|
||||||
|
}
|
||||||
|
if isWinterSemester(currentMonth) {
|
||||||
|
seminarGroup[1], errWS = fetchAndParse("ws", seminarGroupLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time.Now().Month() >= 9) || (time.Now().Month() <= 4) {
|
return checkForSuccessfulFetch(errSS, errWS, seminarGroup)
|
||||||
wsUrl := "https://stundenplan.htwk-leipzig.de/" + string("ws") + "/Berichte/Text-Listen;Studenten-Sets;name;" + seminarGroupLabel + "?template=sws_semgrp&weeks=1-65"
|
}
|
||||||
result, getError := fetch.GetHTML(wsUrl)
|
|
||||||
if getError == nil {
|
func isSummerSemester(month time.Month) bool {
|
||||||
seminarGroup = parseSeminarGroup(result)
|
return month >= 3 && month <= 10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isWinterSemester(month time.Month) bool {
|
||||||
|
return month >= 9 || month <= 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchAndParse(season, label string) (model.SeminarGroup, error) {
|
||||||
|
result, err := fetchHTMLFromURL(season, label)
|
||||||
|
if err != nil {
|
||||||
|
return model.SeminarGroup{}, err
|
||||||
|
}
|
||||||
|
return parseSeminarGroup(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkForSuccessfulFetch(errSS error, errWS error, seminarGroup [2]model.SeminarGroup) (model.SeminarGroup, error) {
|
||||||
|
switch {
|
||||||
|
case errSS != nil && errWS != nil:
|
||||||
|
return model.SeminarGroup{}, errWS
|
||||||
|
case errSS != nil:
|
||||||
|
return seminarGroup[1], nil
|
||||||
|
case errWS != nil:
|
||||||
|
return seminarGroup[0], nil
|
||||||
|
default:
|
||||||
|
seminarGroup[0].Events = append(seminarGroup[0].Events, seminarGroup[1].Events...)
|
||||||
|
return seminarGroup[0], nil
|
||||||
}
|
}
|
||||||
return seminarGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SplitEventType(events []model.Event) ([]model.Event, error) {
|
func SplitEventType(events []model.Event) ([]model.Event, error) {
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_extractSemesterAndYear(t *testing.T) {
|
func TestExtractSemesterAndYear(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
semesterString string
|
semesterString string
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ func Test_extractSemesterAndYear(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_replaceEmptyEventNames(t *testing.T) {
|
func TestReplaceEmptyEventNames(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
group model.SeminarGroup
|
group model.SeminarGroup
|
||||||
}
|
}
|
||||||
@ -130,7 +130,7 @@ func Test_replaceEmptyEventNames(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_splitEventType(t *testing.T) {
|
func TestSplitEventType(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
events []model.Event
|
events []model.Event
|
||||||
}
|
}
|
||||||
@ -220,7 +220,7 @@ func Test_splitEventType(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_generateUUIDs(t *testing.T) {
|
func TestGenerateUUIDs(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
events []model.Event
|
events []model.Event
|
||||||
course string
|
course string
|
||||||
@ -274,7 +274,7 @@ func Test_generateUUIDs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_createTimeFromHourAndMinuteString(t *testing.T) {
|
func TestCreateTimeFromHourAndMinuteString(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
tableTime string
|
tableTime string
|
||||||
}
|
}
|
||||||
@ -314,7 +314,7 @@ func Test_createTimeFromHourAndMinuteString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_replaceTimeInDate(t *testing.T) {
|
func TestReplaceTimeInDate(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
date time.Time
|
date time.Time
|
||||||
time time.Time
|
time time.Time
|
||||||
@ -350,7 +350,7 @@ func Test_replaceTimeInDate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_convertWeeksToDates(t *testing.T) {
|
func TestConvertWeeksToDates(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
events []model.Event
|
events []model.Event
|
||||||
semester string
|
semester string
|
||||||
@ -450,7 +450,7 @@ func Test_convertWeeksToDates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_replaceTimeForDate(t *testing.T) {
|
func TestReplaceTimeForDate(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
date time.Time
|
date time.Time
|
||||||
replacementTime time.Time
|
replacementTime time.Time
|
||||||
@ -501,3 +501,90 @@ func Test_replaceTimeForDate(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsSummerSemester(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
month time.Month
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Test Summer March",
|
||||||
|
args: args{
|
||||||
|
month: time.March,
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test Summer September",
|
||||||
|
args: args{
|
||||||
|
month: time.September,
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := isSummerSemester(tt.args.month); got != tt.want {
|
||||||
|
t.Errorf("isSummerSemester() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsWinterSemester(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
month time.Month
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Test Winter March",
|
||||||
|
args: args{
|
||||||
|
month: time.March,
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test Winter September",
|
||||||
|
args: args{
|
||||||
|
month: time.September,
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test Winter November",
|
||||||
|
args: args{
|
||||||
|
month: time.November,
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test Winter February",
|
||||||
|
args: args{
|
||||||
|
month: time.February,
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test Winter June",
|
||||||
|
args: args{
|
||||||
|
month: time.June,
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := isWinterSemester(tt.args.month); got != tt.want {
|
||||||
|
t.Errorf("isWinterSemester() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_contains(t *testing.T) {
|
func TestContains(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
groups []model.SeminarGroup
|
groups []model.SeminarGroup
|
||||||
group model.SeminarGroup
|
group model.SeminarGroup
|
||||||
|
@ -28,40 +28,48 @@ import (
|
|||||||
func toEvents(tables [][]*html.Node, days []string) []model.Event {
|
func toEvents(tables [][]*html.Node, days []string) []model.Event {
|
||||||
var events []model.Event
|
var events []model.Event
|
||||||
|
|
||||||
for table := range tables {
|
for tableIndex, table := range tables {
|
||||||
for row := range tables[table] {
|
day := days[tableIndex]
|
||||||
|
|
||||||
tableData := findTableData(tables[table][row])
|
for _, row := range table {
|
||||||
if len(tableData) > 0 {
|
tableData := findTableData(row)
|
||||||
start, _ := types.ParseDateTime(date.CreateTimeFromHourAndMinuteString(getTextContent(tableData[1])))
|
if len(tableData) == 0 {
|
||||||
end, _ := types.ParseDateTime(date.CreateTimeFromHourAndMinuteString(getTextContent(tableData[2])))
|
continue
|
||||||
|
|
||||||
courses := getTextContent(tableData[7])
|
|
||||||
name := getTextContent(tableData[3])
|
|
||||||
if functions.OnlyWhitespace(name) {
|
|
||||||
name = "Sonderveranstaltung"
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(courses) > 0 {
|
|
||||||
for _, course := range strings.Split(courses, " ") {
|
|
||||||
events = append(events, model.Event{
|
|
||||||
Day: days[table],
|
|
||||||
Week: getTextContent(tableData[0]),
|
|
||||||
Start: start,
|
|
||||||
End: end,
|
|
||||||
Name: name,
|
|
||||||
EventType: getTextContent(tableData[4]),
|
|
||||||
Notes: getTextContent(tableData[5]),
|
|
||||||
Prof: getTextContent(tableData[6]),
|
|
||||||
Rooms: getTextContent(tableData[8]),
|
|
||||||
BookedAt: getTextContent(tableData[10]),
|
|
||||||
Course: strings.TrimSpace(course),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
events = append(events, createEventsFromTableData(tableData, day)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return events
|
return events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createEventsFromTableData(tableData []*html.Node, day string) []model.Event {
|
||||||
|
var events []model.Event
|
||||||
|
|
||||||
|
start, _ := types.ParseDateTime(date.CreateTimeFromHourAndMinuteString(getTextContent(tableData[1])))
|
||||||
|
end, _ := types.ParseDateTime(date.CreateTimeFromHourAndMinuteString(getTextContent(tableData[2])))
|
||||||
|
name := getTextContent(tableData[3])
|
||||||
|
if functions.OnlyWhitespace(name) {
|
||||||
|
name = "Sonderveranstaltung"
|
||||||
|
}
|
||||||
|
|
||||||
|
courses := getTextContent(tableData[7])
|
||||||
|
if len(courses) > 0 {
|
||||||
|
for _, course := range strings.Split(courses, " ") {
|
||||||
|
events = append(events, model.Event{
|
||||||
|
Day: day,
|
||||||
|
Week: getTextContent(tableData[0]),
|
||||||
|
Start: start,
|
||||||
|
End: end,
|
||||||
|
Name: name,
|
||||||
|
EventType: getTextContent(tableData[4]),
|
||||||
|
Notes: getTextContent(tableData[5]),
|
||||||
|
Prof: getTextContent(tableData[6]),
|
||||||
|
Rooms: getTextContent(tableData[8]),
|
||||||
|
BookedAt: getTextContent(tableData[10]),
|
||||||
|
Course: strings.TrimSpace(course),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_switchNameAndNotesForExam(t *testing.T) {
|
func TestSwitchNameAndNotesForExam(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
events []model.Event
|
events []model.Event
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_calculateSemesterList(t *testing.T) {
|
func TestCalculateSemesterList(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
clock mockTime.Clock
|
clock mockTime.Clock
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_anonymizeRooms(t *testing.T) {
|
func TestAnonymizeRooms(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
events []model.Event
|
events []model.Event
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ func Test_anonymizeRooms(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_isRoomInSchedule(t *testing.T) {
|
func TestIsRoomInSchedule(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
room string
|
room string
|
||||||
schedule []model.Event
|
schedule []model.Event
|
||||||
@ -245,7 +245,7 @@ func Test_isRoomInSchedule(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_getFreeRooms(t *testing.T) {
|
func TestGetFreeRooms(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
rooms []string
|
rooms []string
|
||||||
schedule []model.Event
|
schedule []model.Event
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
sonar.projectKey=HTWKalender
|
sonar.projectKey=HTWKalender
|
||||||
sonar.qualitygate.wait=true
|
sonar.qualitygate.wait=true
|
||||||
|
sonar.exclusions=migrations/**
|
@ -4,13 +4,14 @@ go 1.21
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.9.2
|
github.com/PuerkitoBio/goquery v1.9.2
|
||||||
|
github.com/goccy/go-json v0.10.2
|
||||||
github.com/gofiber/fiber/v3 v3.0.0-beta.2
|
github.com/gofiber/fiber/v3 v3.0.0-beta.2
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/jarcoal/httpmock v1.3.1
|
||||||
github.com/jordic/goics v0.0.0-20210404174824-5a0337b716a0
|
github.com/jordic/goics v0.0.0-20210404174824-5a0337b716a0
|
||||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
||||||
github.com/pocketbase/dbx v1.10.1
|
github.com/pocketbase/dbx v1.10.1
|
||||||
github.com/pocketbase/pocketbase v0.22.12
|
github.com/pocketbase/pocketbase v0.22.12
|
||||||
github.com/samber/slog-fiber v1.15.3
|
|
||||||
golang.org/x/net v0.26.0
|
golang.org/x/net v0.26.0
|
||||||
google.golang.org/grpc v1.63.2
|
google.golang.org/grpc v1.63.2
|
||||||
google.golang.org/protobuf v1.34.1
|
google.golang.org/protobuf v1.34.1
|
||||||
@ -47,8 +48,6 @@ require (
|
|||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/ganigeorgiev/fexpr v0.4.0 // indirect
|
github.com/ganigeorgiev/fexpr v0.4.0 // indirect
|
||||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
|
||||||
github.com/gofiber/fiber/v2 v2.52.1 // indirect
|
|
||||||
github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect
|
github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
@ -60,12 +59,10 @@ require (
|
|||||||
github.com/klauspost/compress v1.17.6 // indirect
|
github.com/klauspost/compress v1.17.6 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/cobra v1.8.0 // indirect
|
github.com/spf13/cobra v1.8.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
@ -74,8 +71,6 @@ require (
|
|||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
|
||||||
gocloud.dev v0.37.0 // indirect
|
gocloud.dev v0.37.0 // indirect
|
||||||
golang.org/x/crypto v0.24.0 // indirect
|
golang.org/x/crypto v0.24.0 // indirect
|
||||||
golang.org/x/image v0.16.0 // indirect
|
golang.org/x/image v0.16.0 // indirect
|
||||||
|
@ -109,8 +109,6 @@ github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVH
|
|||||||
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/gofiber/fiber/v2 v2.52.1 h1:1RoU2NS+b98o1L77sdl5mboGPiW+0Ypsi5oLmcYlgHI=
|
|
||||||
github.com/gofiber/fiber/v2 v2.52.1/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
|
||||||
github.com/gofiber/fiber/v3 v3.0.0-beta.2 h1:mVVgt8PTaHGup3NGl/+7U7nEoZaXJ5OComV4E+HpAao=
|
github.com/gofiber/fiber/v3 v3.0.0-beta.2 h1:mVVgt8PTaHGup3NGl/+7U7nEoZaXJ5OComV4E+HpAao=
|
||||||
github.com/gofiber/fiber/v3 v3.0.0-beta.2/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM=
|
github.com/gofiber/fiber/v3 v3.0.0-beta.2/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM=
|
||||||
github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co=
|
github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co=
|
||||||
@ -162,6 +160,8 @@ github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u
|
|||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
|
||||||
|
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
@ -187,11 +187,11 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
|
||||||
|
github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
@ -206,13 +206,9 @@ github.com/pocketbase/pocketbase v0.22.12/go.mod h1:yY/3IGi1tUbcI6yGVFspAyKi/IDH
|
|||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/samber/slog-fiber v1.15.3 h1:RbfC0v2QPIEXoRdort2QxAsRG42LVaFTEgTNS/0GwRQ=
|
|
||||||
github.com/samber/slog-fiber v1.15.3/go.mod h1:I0b8eJ060SlpA65LXiqH7lZixUCkAPKiEGZqkT9QJOM=
|
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_calculateSemesterList(t *testing.T) {
|
func TestCalculateSemesterList(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
clock mockTime.Clock
|
clock mockTime.Clock
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user