forgejo-runner

git clone git://git.lin.moe/forgejo-runner.git

  1// Copyright 2023 The Gitea Authors. All rights reserved.
  2// SPDX-License-Identifier: MIT
  3
  4package report
  5
  6import (
  7	"context"
  8	"strings"
  9	"testing"
 10	"time"
 11
 12	runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
 13	connect_go "connectrpc.com/connect"
 14	log "github.com/sirupsen/logrus"
 15	"github.com/stretchr/testify/assert"
 16	"github.com/stretchr/testify/mock"
 17	"github.com/stretchr/testify/require"
 18	"google.golang.org/protobuf/types/known/structpb"
 19
 20	"gitea.com/gitea/act_runner/internal/pkg/client/mocks"
 21)
 22
 23func TestReporter_parseLogRow(t *testing.T) {
 24	tests := []struct {
 25		name               string
 26		debugOutputEnabled bool
 27		args               []string
 28		want               []string
 29	}{
 30		{
 31			"No command", false,
 32			[]string{"Hello, world!"},
 33			[]string{"Hello, world!"},
 34		},
 35		{
 36			"Add-mask", false,
 37			[]string{
 38				"foo mysecret bar",
 39				"::add-mask::mysecret",
 40				"foo mysecret bar",
 41			},
 42			[]string{
 43				"foo mysecret bar",
 44				"<nil>",
 45				"foo *** bar",
 46			},
 47		},
 48		{
 49			"Debug enabled", true,
 50			[]string{
 51				"::debug::GitHub Actions runtime token access controls",
 52			},
 53			[]string{
 54				"GitHub Actions runtime token access controls",
 55			},
 56		},
 57		{
 58			"Debug not enabled", false,
 59			[]string{
 60				"::debug::GitHub Actions runtime token access controls",
 61			},
 62			[]string{
 63				"<nil>",
 64			},
 65		},
 66		{
 67			"notice", false,
 68			[]string{
 69				"::notice file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
 70			},
 71			[]string{
 72				"::notice file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
 73			},
 74		},
 75		{
 76			"warning", false,
 77			[]string{
 78				"::warning file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
 79			},
 80			[]string{
 81				"::warning file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
 82			},
 83		},
 84		{
 85			"error", false,
 86			[]string{
 87				"::error file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
 88			},
 89			[]string{
 90				"::error file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
 91			},
 92		},
 93		{
 94			"group", false,
 95			[]string{
 96				"::group::",
 97				"::endgroup::",
 98			},
 99			[]string{
100				"##[group]",
101				"##[endgroup]",
102			},
103		},
104		{
105			"stop-commands", false,
106			[]string{
107				"::add-mask::foo",
108				"::stop-commands::myverycoolstoptoken",
109				"::add-mask::bar",
110				"::debug::Stuff",
111				"myverycoolstoptoken",
112				"::add-mask::baz",
113				"::myverycoolstoptoken::",
114				"::add-mask::wibble",
115				"foo bar baz wibble",
116			},
117			[]string{
118				"<nil>",
119				"<nil>",
120				"::add-mask::bar",
121				"::debug::Stuff",
122				"myverycoolstoptoken",
123				"::add-mask::baz",
124				"<nil>",
125				"<nil>",
126				"*** bar baz ***",
127			},
128		},
129		{
130			"unknown command", false,
131			[]string{
132				"::set-mask::foo",
133			},
134			[]string{
135				"::set-mask::foo",
136			},
137		},
138	}
139	for _, tt := range tests {
140		t.Run(tt.name, func(t *testing.T) {
141			r := &Reporter{
142				logReplacer:        strings.NewReplacer(),
143				debugOutputEnabled: tt.debugOutputEnabled,
144			}
145			for idx, arg := range tt.args {
146				rv := r.parseLogRow(&log.Entry{Message: arg})
147				got := "<nil>"
148
149				if rv != nil {
150					got = rv.Content
151				}
152
153				assert.Equal(t, tt.want[idx], got)
154			}
155		})
156	}
157}
158
159func TestReporter_Fire(t *testing.T) {
160	t.Run("ignore command lines", func(t *testing.T) {
161		client := mocks.NewClient(t)
162		client.On("UpdateLog", mock.Anything, mock.Anything).Return(func(_ context.Context, req *connect_go.Request[runnerv1.UpdateLogRequest]) (*connect_go.Response[runnerv1.UpdateLogResponse], error) {
163			t.Logf("Received UpdateLog: %s", req.Msg.String())
164			return connect_go.NewResponse(&runnerv1.UpdateLogResponse{
165				AckIndex: req.Msg.Index + int64(len(req.Msg.Rows)),
166			}), nil
167		})
168		client.On("UpdateTask", mock.Anything, mock.Anything).Return(func(_ context.Context, req *connect_go.Request[runnerv1.UpdateTaskRequest]) (*connect_go.Response[runnerv1.UpdateTaskResponse], error) {
169			t.Logf("Received UpdateTask: %s", req.Msg.String())
170			return connect_go.NewResponse(&runnerv1.UpdateTaskResponse{}), nil
171		})
172		ctx, cancel := context.WithCancel(context.Background())
173		taskCtx, err := structpb.NewStruct(map[string]interface{}{})
174		require.NoError(t, err)
175		reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{
176			Context: taskCtx,
177		}, time.Second)
178		defer func() {
179			assert.NoError(t, reporter.Close(""))
180		}()
181		reporter.ResetSteps(5)
182
183		dataStep0 := map[string]interface{}{
184			"stage":      "Main",
185			"stepNumber": 0,
186			"raw_output": true,
187		}
188
189		assert.NoError(t, reporter.Fire(&log.Entry{Message: "regular log line", Data: dataStep0}))
190		assert.NoError(t, reporter.Fire(&log.Entry{Message: "::debug::debug log line", Data: dataStep0}))
191		assert.NoError(t, reporter.Fire(&log.Entry{Message: "regular log line", Data: dataStep0}))
192		assert.NoError(t, reporter.Fire(&log.Entry{Message: "::debug::debug log line", Data: dataStep0}))
193		assert.NoError(t, reporter.Fire(&log.Entry{Message: "::debug::debug log line", Data: dataStep0}))
194		assert.NoError(t, reporter.Fire(&log.Entry{Message: "regular log line", Data: dataStep0}))
195
196		assert.Equal(t, int64(3), reporter.state.Steps[0].LogLength)
197	})
198}