Merge branch '24-add-text-template-task' into 'master'

Resolve "add text/template task"

Closes #24

See merge request slon/shad-go-private!37
This commit is contained in:
verytable 2021-02-18 19:47:04 +00:00
commit f70c329549
5 changed files with 339 additions and 0 deletions

62
ciletters/README.md Normal file
View file

@ -0,0 +1,62 @@
## ciletters
В этой задаче вам предстоит познакомиться со стандартным пакетом [text/template](https://golang.org/pkg/text/template/),
позволяющим генерировать текст в определенном формате.
### Легенда
В gitlab можно подписаться на различные события: успешный build, новый комментарий, решённое issue и др.
Вот так, например, выглядит нотификация о сломанном pipeline'е:
![Image description](assets/notification.png)
В задаче предлагается сгенерировать письмо подобного содержания в текстовом виде.
Для генерации HTML в языке также имеется стандартный пакет [html/template](https://golang.org/pkg/html/template/).
Работа с ним аналогична работа с `text/template`, поэтому для простоты в задаче был выбран тестовый формат.
### Что нужно сделать?
Нужно реализовать функцию `MakeLetter` из файла [letter.go](./letter.go),
которая по go объекту нотификации генерирует её текстовое представление.
#### Прокомментированный пример из теста
```
Your pipeline #194613 has failed! // 194613 -- это ID pipeline'а
Project: go-spring-2021/gopher // Project состоит из ID группы и ID проекта
Branch: 🌿 master
Commit: 8967153e Solve urlfetch. // Первые 8 байт хэша коммита.
CommitAuthor: gopher
// Здесь происходит цикл по всем сломанным job'ам
Stage: test, Job grade // test -- это имя stage'а, а grade -- имя job'а
// Далее идут последние 10 строк лога gitlab runner'а
testtool: copying go.mod, go.sum and .golangci.yml
testtool: running tests
testtool: > go test -mod readonly -tags private -c -o /tmp/bincache730817117/5d83984f885e61c1 gitlab.com/slon/shad-go/sum
--- FAIL: TestSum (0.00s)
sum_test.go:19: 2 + 2 == 0 != 4
sum_test.go:19: 9223372036854775807 + 1 == 0 != -9223372036854775808
FAIL
testtool: task sum failed: test failed: exit status 1
some tasks failed
ERROR: Job failed: exit code 1
```
Объект нотификации описан в [notification.go](notification.go).
Обратите внимание на `// +build !change`.
Этот файл менять не нужно, и на сервере будет использоваться оригинальный вариант.
В реализации нужно подогнать `text/template` шаблон под требуемый вывод.
Вам могут понадобиться:
* условные блоки (`if/else`)
* range'и
* кастомные функции в шаблоне: https://golang.org/pkg/text/template/#FuncMap
* `'-'` для удаления пробелов
### Проверка решения
Для запуска тестов нужно выполнить следующую команду:
```
go test -v ./ciletters/...
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

7
ciletters/letter.go Normal file
View file

@ -0,0 +1,7 @@
// +build !solution
package ciletters
func MakeLetter(n *Notification) (string, error) {
return "", nil
}

227
ciletters/letter_test.go Normal file
View file

@ -0,0 +1,227 @@
package ciletters
import (
"crypto/rand"
"fmt"
"math/big"
"testing"
"github.com/stretchr/testify/require"
"gitlab.com/slon/shad-go/tools/testtool"
)
type testCase struct {
name string
notification Notification
expected string
}
func TestSpell(t *testing.T) {
const (
testUser = "gopher"
gitlabGroupID = "go-spring-2021"
)
randomGitlabGroup := testtool.RandomName()
randomGitlabProject := testtool.RandomName()
randomBranch := testtool.RandomName()
randomTestUser := testtool.RandomName()
randomTriggerer := testtool.RandomName()
randomPipelineID := randomInt64(t)
randomJobIDS := []int64{randomInt64(t), randomInt64(t)}
randomJobNames := []string{testtool.RandomName(), testtool.RandomName()}
randomStages := []string{testtool.RandomName(), testtool.RandomName()}
randomHash := testtool.RandomName()[:8]
for _, tc := range []testCase{
{
name: "success",
notification: Notification{
Project: GitlabProject{
GroupID: gitlabGroupID,
ID: testUser,
},
Branch: "master",
Commit: Commit{
Hash: "2ff019bcb8f68d13d640e13351dad98edf7f1405",
Message: "Solve sum.",
Author: testUser,
},
Pipeline: Pipeline{
Status: PipelineStatusOK,
ID: 194555,
TriggeredBy: testUser,
},
},
expected: `Your pipeline #194555 passed!
Project: go-spring-2021/gopher
Branch: 🌿 master
Commit: 2ff019bc Solve sum.
CommitAuthor: gopher`,
},
{
name: "job-failed",
notification: Notification{
Project: GitlabProject{
GroupID: gitlabGroupID,
ID: testUser,
},
Branch: "master",
Commit: Commit{
Hash: "8967153e8aa7b270af6447dae594eb87bdae8791",
Message: "Solve urlfetch.",
Author: testUser,
},
Pipeline: Pipeline{
Status: PipelineStatusFailed,
ID: 194613,
TriggeredBy: testUser,
FailedJobs: []Job{
{
ID: 202538,
Name: "grade",
Stage: "test",
RunnerLog: `$ testtool grade
testtool: detected change in tasks [sum]
testtool: skipping task sum: not released yet
testtool: testing task sum
testtool: testing submission in /tmp/sum-281145206
testtool: copying student repo
testtool: copying tests
testtool: copying !change files
testtool: copying testdata directory
testtool: copying go.mod, go.sum and .golangci.yml
testtool: running tests
testtool: > go test -mod readonly -tags private -c -o /tmp/bincache730817117/5d83984f885e61c1 gitlab.com/slon/shad-go/sum
--- FAIL: TestSum (0.00s)
sum_test.go:19: 2 + 2 == 0 != 4
sum_test.go:19: 9223372036854775807 + 1 == 0 != -9223372036854775808
FAIL
testtool: task sum failed: test failed: exit status 1
some tasks failed
ERROR: Job failed: exit code 1`,
},
},
},
},
expected: `Your pipeline #194613 has failed!
Project: go-spring-2021/gopher
Branch: 🌿 master
Commit: 8967153e Solve urlfetch.
CommitAuthor: gopher
Stage: test, Job grade
testtool: copying go.mod, go.sum and .golangci.yml
testtool: running tests
testtool: > go test -mod readonly -tags private -c -o /tmp/bincache730817117/5d83984f885e61c1 gitlab.com/slon/shad-go/sum
--- FAIL: TestSum (0.00s)
sum_test.go:19: 2 + 2 == 0 != 4
sum_test.go:19: 9223372036854775807 + 1 == 0 != -9223372036854775808
FAIL
testtool: task sum failed: test failed: exit status 1
some tasks failed
ERROR: Job failed: exit code 1
`,
},
{
name: "multiple-jobs-failed",
notification: Notification{
Project: GitlabProject{
GroupID: randomGitlabGroup,
ID: randomGitlabProject,
},
Branch: randomBranch,
Commit: Commit{
Hash: randomHash,
Message: "Solve digitalclock.",
Author: randomTestUser,
},
Pipeline: Pipeline{
Status: PipelineStatusFailed,
ID: randomPipelineID,
TriggeredBy: randomTriggerer,
FailedJobs: []Job{
{
ID: randomJobIDS[0],
Name: randomJobNames[0],
Stage: randomStages[0],
RunnerLog: `$ testtool grade
testtool: detected change in tasks [sum]
testtool: skipping task sum: not released yet
testtool: testing task sum
testtool: testing submission in /tmp/sum-281145206
testtool: copying student repo
testtool: copying tests
testtool: copying !change files
testtool: copying testdata directory
testtool: copying go.mod, go.sum and .golangci.yml
testtool: running tests
testtool: > go test -mod readonly -tags private -c -o /tmp/bincache730817117/5d83984f885e61c1 gitlab.com/slon/shad-go/sum
--- FAIL: TestSum (0.00s)
sum_test.go:19: 2 + 2 == 0 != 4
sum_test.go:19: 9223372036854775807 + 1 == 0 != -9223372036854775808
FAIL
testtool: task sum failed: test failed: exit status 1
some tasks failed
ERROR: Job failed: exit code 1`,
},
{
ID: randomJobIDS[1],
Name: randomJobNames[1],
Stage: randomStages[1],
RunnerLog: `--- FAIL: TestSum (0.00s)
sum_test.go:19: 2 + 2 == 0 != 4
sum_test.go:19: 9223372036854775807 + 1 == 0 != -9223372036854775808
FAIL
testtool: task sum failed: test failed: exit status 1
some tasks failed
ERROR: Job failed: exit code 1`,
},
},
},
},
expected: fmt.Sprintf(`Your pipeline #%d has failed!
Project: %v/%v
Branch: 🌿 %v
Commit: %v Solve digitalclock.
CommitAuthor: %v
Stage: %v, Job %v
testtool: copying go.mod, go.sum and .golangci.yml
testtool: running tests
testtool: > go test -mod readonly -tags private -c -o /tmp/bincache730817117/5d83984f885e61c1 gitlab.com/slon/shad-go/sum
--- FAIL: TestSum (0.00s)
sum_test.go:19: 2 + 2 == 0 != 4
sum_test.go:19: 9223372036854775807 + 1 == 0 != -9223372036854775808
FAIL
testtool: task sum failed: test failed: exit status 1
some tasks failed
ERROR: Job failed: exit code 1
Stage: %v, Job %v
--- FAIL: TestSum (0.00s)
sum_test.go:19: 2 + 2 == 0 != 4
sum_test.go:19: 9223372036854775807 + 1 == 0 != -9223372036854775808
FAIL
testtool: task sum failed: test failed: exit status 1
some tasks failed
ERROR: Job failed: exit code 1
`, randomPipelineID, randomGitlabGroup, randomGitlabProject, randomBranch, randomHash, randomTestUser,
randomStages[0], randomJobNames[0], randomStages[1], randomJobNames[1]),
},
} {
t.Run(tc.name, func(t *testing.T) {
letter, err := MakeLetter(&tc.notification)
require.NoError(t, err)
require.Equal(t, tc.expected, letter)
})
}
}
func randomInt64(t *testing.T) int64 {
t.Helper()
nBig, err := rand.Int(rand.Reader, big.NewInt(1e6))
require.NoError(t, err)
return nBig.Int64()
}

43
ciletters/notification.go Normal file
View file

@ -0,0 +1,43 @@
// +build !change
package ciletters
type Notification struct {
Project GitlabProject
Branch string
Commit Commit
Pipeline Pipeline
}
type GitlabProject struct {
GroupID string
ID string
}
type Commit struct {
// Hash is a 20-byte SHA-1 encoded in hex.
Hash string
Message string
Author string
}
type PipelineStatus string
const (
PipelineStatusOK PipelineStatus = "ok"
PipelineStatusFailed PipelineStatus = "failed"
)
type Pipeline struct {
Status PipelineStatus
ID int64
TriggeredBy string
FailedJobs []Job
}
type Job struct {
ID int64
Name string
Stage string
RunnerLog string
}