diff --git a/example scripts/svod.spec.js b/example scripts/svod.spec.js new file mode 100644 index 0000000..44a6111 --- /dev/null +++ b/example scripts/svod.spec.js @@ -0,0 +1,107 @@ +import { test, expect } from '@playwright/test'; + +test('Тест свод отчетов', async ({ page }) => { + test.setTimeout(100_000); // секунд только для этого теста + + await page.goto('https://digital.zo.gov.ru/sko/ru/'); + await page.locator('#userName').click(); + await page.locator('#userName').fill('АдминистраторП'); + await page.locator('.authPassInput').first().click(); + await page.locator('#userPassword').click(); + await page.locator('#userPassword').fill('Ho0de3vi'); + await page.getByRole('button', { name: 'Войти' }).click(); + + await page.getByText('Ф. 0503737').nth(2).dblclick(); + await close(page); + + await page.waitForTimeout(100); + await page.getByText('Нормативно-справочная').click(); + await page.waitForTimeout(100); + + await page.getByText('Бюджеты').click(); + await page.waitForTimeout(100); + + await doubleClickRandomRow(page); + await closeButton(page, 'ФормаЗаписатьИЗакрыть') + + const count = await page.locator('id$="_CommandButtonOK"'); + if(count > 0) { + await page.locator('id$="_CommandButtonOK"').click(); + await closeButton(page, 'ФормаЗаписатьИЗакрыть') + } + + await page.waitForTimeout(500); + await page.getByText('Анализ данных').click(); + await page.waitForTimeout(500); // пауза + await page.locator('#cmd_0_0_txt').click(); + await page.waitForTimeout(200); + await close(page); + + await page.getByText('Комплект отчетности').click(); + await page.waitForTimeout(200); + await page.locator('#cmd_2_0_txt').click(); + await page.waitForTimeout(1000); + + await page.locator('a[id^="form"][id$="СформироватьОтчет"]').last().click(); + await page.waitForTimeout(1000); + await close(page); +}); + +function randomString() { + return Math.random().toString(36).substring(2, 10); +} + +async function closeButton(page, name) { + const elem = await page.locator(`a[id$="_${name}"]`); + await elem.waitFor(); + + const elems = await elem.elementHandles(); + + console.log(name, elems.length) + + for (const btn of elems) { + const id = await btn.evaluate(el => el.id); + if (new RegExp(`^form\\d+_${name}$`).test(id)) { + await btn.click(); + break; // кликнули первый подходящий + } + } +} + +async function doubleClickRandomRow(page){ + // Получаем все строки + const rows = await page.locator('div.gridBody div.gridLine').all(); + if (rows.length === 0) { + console.log(`В списке нет строк`); + return + } + + await page.waitForTimeout(200); + + // Фильтруем только видимые строки + const visibleRows = []; + for (const row of rows) { + if (await row.isVisible()) { + // const text = await row.innerText(); + // console.log(text); + visibleRows.push(row); + } + } + + // Выбираем случайную из видимых + const randomIndex = Math.floor(Math.random() * visibleRows.length); + const targetRow = visibleRows[randomIndex]; + + // Двойной клик + await targetRow.dblclick({ timeout: 5000 }); + + console.log(`Выполнен двойной клик на строку с индексом: ${randomIndex} из ${rows.length}`); +} + +async function close(page) { + try { + await page.locator('[id^="VW_page"][id$="headerTopLine_cmd_CloseButton"]').last().click({ timeout: 10000 }); + } catch (error) { + console.warn('⚠️ Кнопка закрытия не найдена или не кликабельна:', error.message); + } +} diff --git a/observer/internal/app/worker.go b/observer/internal/app/worker.go index b06b53d..d2e74fc 100644 --- a/observer/internal/app/worker.go +++ b/observer/internal/app/worker.go @@ -22,7 +22,7 @@ type Worker struct { ParallelTests int `json:"parallel_tests"` ws WS `json:"-"` client gen.WorkerClient `json:"-"` - mx sync.Mutex `json:"-"` + mx sync.RWMutex `json:"-"` } func (w *Worker) ChangeState(newState state) { @@ -30,7 +30,10 @@ func (w *Worker) ChangeState(newState state) { defer w.mx.Unlock() w.Status = newState + w.sendWorkerToFront() +} +func (w *Worker) sendWorkerToFront() { data, _ := json.Marshal(w) if err := w.ws.WriteWSMessage(string(data)); err != nil { log.Println("WriteWSMessage error:", err) @@ -64,8 +67,11 @@ func (w *Worker) Stop() error { } func (w *Worker) SetTestScript(script string) error { + w.mx.Lock() + defer w.mx.Unlock() + w.Script = script - _, err := w.client.SetTestScript(context.Background(), &gen.SetTestScriptReq{Script: script}) + _, err := w.client.SetTestScript(context.Background(), &gen.TestScript{Script: script}) return err } @@ -90,15 +96,32 @@ func (w *Worker) grpcStart(ctx context.Context, chanStatus chan<- WorkerStatus) defer conn.Close() w.client = gen.NewWorkerClient(conn) - w.grpcKeepalive(ctx, w.client, chanStatus) + w.grpcKeepalive(ctx, chanStatus) } -func (w *Worker) grpcKeepalive(ctx context.Context, client gen.WorkerClient, chanStatus chan<- WorkerStatus) { +func (w *Worker) syncScript(ctx context.Context) { + w.mx.Lock() + defer w.mx.Unlock() + + if w.Script == "" { + if script, err := w.client.GetTestScript(ctx, &gen.Empty{}); err == nil { + w.Script = script.Script + } + } else { + _, _ = w.client.SetTestScript(ctx, &gen.TestScript{Script: w.Script}) + } +} + +func (w *Worker) grpcKeepalive(ctx context.Context, chanStatus chan<- WorkerStatus) { for { - stream, err := client.ObserverChangeState(ctx, &gen.Empty{}) + stream, err := w.client.ObserverChangeState(ctx, &gen.Empty{}) if err != nil { log.Println("GRPC error:", err) } else { + // сразу при подключении синкаем скрипт потому что мог быть перезагружен воркер тогда мы скрипт отдает + // а мог быть перезагружен observer тогда мы скрипт забираем + w.syncScript(ctx) + chanStatus <- WorkerStatus{workerID: w.Id, status: gen.WorkerStatus_STATE_READY} w.readStream(stream, w.Id, chanStatus) } diff --git a/worker/grpc/server.go b/worker/grpc/server.go index 7cf5bcf..24a143e 100644 --- a/worker/grpc/server.go +++ b/worker/grpc/server.go @@ -34,7 +34,7 @@ func NewGRPCServer(ctx context.Context, port int, worker gen.WorkerServer) error func logInterceptor(logger *slog.Logger) func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) { return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) { - _, err = handler(ctx, req) + resp, err = handler(ctx, req) if err != nil { logger.ErrorContext(ctx, "grpc error", "error", err) } else { diff --git a/worker/internal/app/job.go b/worker/internal/app/job.go index 89f54d5..48c293e 100644 --- a/worker/internal/app/job.go +++ b/worker/internal/app/job.go @@ -2,20 +2,22 @@ package app import ( "context" + "errors" "fmt" - "github.com/hashicorp/go-multierror" + "github.com/samber/lo" "github.com/sourcegraph/conc" ) func (w *Worker) startJob(ctx context.Context, testCount int32) error { w.logger.InfoContext(ctx, fmt.Sprintf("start worker, test count %d", testCount)) - err := new(multierror.Error) + var err error var wg conc.WaitGroup for range testCount { wg.Go(func() { if e := w.runTest(ctx, w.playwrightDir); e != nil { - err = multierror.Append(err, e) + w.logger.ErrorContext(ctx, e.Error()) + err = lo.If(err == nil, errors.New("one or more tests failed with an error")).Else(err) return } w.logger.InfoContext(ctx, "test is pass") @@ -23,5 +25,5 @@ func (w *Worker) startJob(ctx context.Context, testCount int32) error { } wg.Wait() - return err.ErrorOrNil() + return err } diff --git a/worker/internal/app/playwright.go b/worker/internal/app/playwright.go index c75d8ba..d042f26 100644 --- a/worker/internal/app/playwright.go +++ b/worker/internal/app/playwright.go @@ -5,18 +5,23 @@ import ( "embed" "fmt" "github.com/pkg/errors" + "math/rand/v2" "os" "os/exec" "path/filepath" "regexp" "strings" + "time" ) //go:embed resource/* var staticFS embed.FS func (w *Worker) runTest(ctx context.Context, playwrightDir string) error { - w.logger.InfoContext(ctx, "exec run playwright test") + // небольшая рандомная задержка + time.Sleep(time.Duration(rand.IntN(5)) * time.Second) + + w.logger.DebugContext(ctx, "exec run playwright test") if strings.TrimSpace(w.script) == "" { return errors.New("script not filled ") @@ -87,15 +92,6 @@ func (w *Worker) create(ctx context.Context, rootDir string) error { return nil } -func replacePlaywrightConfig(rootDir string) error { - data, err := staticFS.ReadFile("resource/playwright.config.js") - if err != nil { - return err - } - - targetPath := filepath.Join(rootDir, "playwright.config.js") - return os.WriteFile(targetPath, data, 0o644) -} func (w *Worker) cmdRun(ctx context.Context, cmd *exec.Cmd) ([]byte, error) { w.stopProcess(ctx, cmd) @@ -128,6 +124,16 @@ func (w *Worker) cmdRun(ctx context.Context, cmd *exec.Cmd) ([]byte, error) { return out, nil } +func replacePlaywrightConfig(rootDir string) error { + data, err := staticFS.ReadFile("resource/playwright.config.js") + if err != nil { + return err + } + + targetPath := filepath.Join(rootDir, "playwright.config.js") + return os.WriteFile(targetPath, data, 0o644) +} + // npx create-playwright@latest --quiet --lang=js --install-deps --gha // npx playwright install // npx playwright uninstall --all diff --git a/worker/internal/app/run_windows.go b/worker/internal/app/run_windows.go index c4c34b0..b6e462c 100644 --- a/worker/internal/app/run_windows.go +++ b/worker/internal/app/run_windows.go @@ -15,7 +15,7 @@ func (w *Worker) stopProcess(ctx context.Context, cmd *exec.Cmd) { go func() { <-ctx.Done() - w.logger.WarnContext(ctx, "context canceled -> terminating process group") + //w.logger.WarnContext(ctx, "context canceled -> terminating process group") // Отправляем Ctrl-Break всей группе процессов _ = windows.GenerateConsoleCtrlEvent(syscall.CTRL_BREAK_EVENT, uint32(cmd.Process.Pid)) diff --git a/worker/internal/app/worker.go b/worker/internal/app/worker.go index fc72e8d..6d8746c 100644 --- a/worker/internal/app/worker.go +++ b/worker/internal/app/worker.go @@ -10,6 +10,7 @@ import ( "log/slog" "os" "path/filepath" + "sync" ) type Worker struct { @@ -20,6 +21,7 @@ type Worker struct { playwrightDir string script string logger *slog.Logger + mx sync.RWMutex } func NewWorker() *Worker { @@ -65,12 +67,21 @@ func dirExists(path string) bool { return err == nil } -func (w *Worker) SetTestScript(_ context.Context, req *gen.SetTestScriptReq) (*gen.Empty, error) { - w.script = req.Script +func (w *Worker) SetTestScript(_ context.Context, req *gen.TestScript) (*gen.Empty, error) { + w.mx.Lock() + defer w.mx.Unlock() + w.script = req.Script return new(gen.Empty), nil } +func (w *Worker) GetTestScript(_ context.Context, _ *gen.Empty) (*gen.TestScript, error) { + w.mx.RLock() + defer w.mx.RUnlock() + + return &gen.TestScript{Script: w.script}, nil +} + func (w *Worker) Start(ctxParent context.Context, resp *gen.StartResp) (_ *gen.Empty, err error) { defer func() { if err != nil { diff --git a/worker/proto/gen/worker.pb.go b/worker/proto/gen/worker.pb.go index c699952..3ea313d 100644 --- a/worker/proto/gen/worker.pb.go +++ b/worker/proto/gen/worker.pb.go @@ -161,27 +161,27 @@ func (x *StartResp) GetTestCount() int32 { return 0 } -type SetTestScriptReq struct { +type TestScript struct { state protoimpl.MessageState `protogen:"open.v1"` Script string `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *SetTestScriptReq) Reset() { - *x = SetTestScriptReq{} +func (x *TestScript) Reset() { + *x = TestScript{} mi := &file_worker_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *SetTestScriptReq) String() string { +func (x *TestScript) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SetTestScriptReq) ProtoMessage() {} +func (*TestScript) ProtoMessage() {} -func (x *SetTestScriptReq) ProtoReflect() protoreflect.Message { +func (x *TestScript) ProtoReflect() protoreflect.Message { mi := &file_worker_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -193,12 +193,12 @@ func (x *SetTestScriptReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SetTestScriptReq.ProtoReflect.Descriptor instead. -func (*SetTestScriptReq) Descriptor() ([]byte, []int) { +// Deprecated: Use TestScript.ProtoReflect.Descriptor instead. +func (*TestScript) Descriptor() ([]byte, []int) { return file_worker_proto_rawDescGZIP(), []int{2} } -func (x *SetTestScriptReq) GetScript() string { +func (x *TestScript) GetScript() string { if x != nil { return x.Script } @@ -251,17 +251,19 @@ const file_worker_proto_rawDesc = "" + "\x06status\x18\x04 \x01(\x0e2\r.WorkerStatusR\x06status\"*\n" + "\tStartResp\x12\x1d\n" + "\n" + - "test_count\x18\x04 \x01(\x05R\ttestCount\"*\n" + - "\x10SetTestScriptReq\x12\x16\n" + + "test_count\x18\x04 \x01(\x05R\ttestCount\"$\n" + + "\n" + + "TestScript\x12\x16\n" + "\x06script\x18\x01 \x01(\tR\x06script\"\a\n" + "\x05Empty*Z\n" + "\fWorkerStatus\x12\x15\n" + "\x11STATE_UNSPECIFIED\x10\x00\x12\x0f\n" + "\vSTATE_READY\x10\x01\x12\x11\n" + "\rSTATE_RUNNING\x10\x02\x12\x0f\n" + - "\vSTATE_ERROR\x10\x032\x9f\x01\n" + - "\x06Worker\x12,\n" + - "\rSetTestScript\x12\x11.SetTestScriptReq\x1a\x06.Empty\"\x00\x12\x1d\n" + + "\vSTATE_ERROR\x10\x032\xc1\x01\n" + + "\x06Worker\x12&\n" + + "\rSetTestScript\x12\v.TestScript\x1a\x06.Empty\"\x00\x12&\n" + + "\rGetTestScript\x12\x06.Empty\x1a\v.TestScript\"\x00\x12\x1d\n" + "\x05Start\x12\n" + ".StartResp\x1a\x06.Empty\"\x00\x12\x18\n" + "\x04Stop\x12\x06.Empty\x1a\x06.Empty\"\x00\x12.\n" + @@ -282,24 +284,26 @@ func file_worker_proto_rawDescGZIP() []byte { var file_worker_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_worker_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_worker_proto_goTypes = []any{ - (WorkerStatus)(0), // 0: WorkerStatus - (*StatusInfo)(nil), // 1: StatusInfo - (*StartResp)(nil), // 2: StartResp - (*SetTestScriptReq)(nil), // 3: SetTestScriptReq - (*Empty)(nil), // 4: Empty + (WorkerStatus)(0), // 0: WorkerStatus + (*StatusInfo)(nil), // 1: StatusInfo + (*StartResp)(nil), // 2: StartResp + (*TestScript)(nil), // 3: TestScript + (*Empty)(nil), // 4: Empty } var file_worker_proto_depIdxs = []int32{ 0, // 0: StatusInfo.status:type_name -> WorkerStatus - 3, // 1: Worker.SetTestScript:input_type -> SetTestScriptReq - 2, // 2: Worker.Start:input_type -> StartResp - 4, // 3: Worker.Stop:input_type -> Empty - 4, // 4: Worker.ObserverChangeState:input_type -> Empty - 4, // 5: Worker.SetTestScript:output_type -> Empty - 4, // 6: Worker.Start:output_type -> Empty - 4, // 7: Worker.Stop:output_type -> Empty - 1, // 8: Worker.ObserverChangeState:output_type -> StatusInfo - 5, // [5:9] is the sub-list for method output_type - 1, // [1:5] is the sub-list for method input_type + 3, // 1: Worker.SetTestScript:input_type -> TestScript + 4, // 2: Worker.GetTestScript:input_type -> Empty + 2, // 3: Worker.Start:input_type -> StartResp + 4, // 4: Worker.Stop:input_type -> Empty + 4, // 5: Worker.ObserverChangeState:input_type -> Empty + 4, // 6: Worker.SetTestScript:output_type -> Empty + 3, // 7: Worker.GetTestScript:output_type -> TestScript + 4, // 8: Worker.Start:output_type -> Empty + 4, // 9: Worker.Stop:output_type -> Empty + 1, // 10: Worker.ObserverChangeState:output_type -> StatusInfo + 6, // [6:11] is the sub-list for method output_type + 1, // [1:6] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name diff --git a/worker/proto/gen/worker_grpc.pb.go b/worker/proto/gen/worker_grpc.pb.go index b5c6e1b..0b4da32 100644 --- a/worker/proto/gen/worker_grpc.pb.go +++ b/worker/proto/gen/worker_grpc.pb.go @@ -20,6 +20,7 @@ const _ = grpc.SupportPackageIsVersion9 const ( Worker_SetTestScript_FullMethodName = "/Worker/SetTestScript" + Worker_GetTestScript_FullMethodName = "/Worker/GetTestScript" Worker_Start_FullMethodName = "/Worker/Start" Worker_Stop_FullMethodName = "/Worker/Stop" Worker_ObserverChangeState_FullMethodName = "/Worker/ObserverChangeState" @@ -29,7 +30,8 @@ const ( // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type WorkerClient interface { - SetTestScript(ctx context.Context, in *SetTestScriptReq, opts ...grpc.CallOption) (*Empty, error) + SetTestScript(ctx context.Context, in *TestScript, opts ...grpc.CallOption) (*Empty, error) + GetTestScript(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TestScript, error) Start(ctx context.Context, in *StartResp, opts ...grpc.CallOption) (*Empty, error) Stop(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) ObserverChangeState(ctx context.Context, in *Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StatusInfo], error) @@ -43,7 +45,7 @@ func NewWorkerClient(cc grpc.ClientConnInterface) WorkerClient { return &workerClient{cc} } -func (c *workerClient) SetTestScript(ctx context.Context, in *SetTestScriptReq, opts ...grpc.CallOption) (*Empty, error) { +func (c *workerClient) SetTestScript(ctx context.Context, in *TestScript, opts ...grpc.CallOption) (*Empty, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Empty) err := c.cc.Invoke(ctx, Worker_SetTestScript_FullMethodName, in, out, cOpts...) @@ -53,6 +55,16 @@ func (c *workerClient) SetTestScript(ctx context.Context, in *SetTestScriptReq, return out, nil } +func (c *workerClient) GetTestScript(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TestScript, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TestScript) + err := c.cc.Invoke(ctx, Worker_GetTestScript_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *workerClient) Start(ctx context.Context, in *StartResp, opts ...grpc.CallOption) (*Empty, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Empty) @@ -96,7 +108,8 @@ type Worker_ObserverChangeStateClient = grpc.ServerStreamingClient[StatusInfo] // All implementations must embed UnimplementedWorkerServer // for forward compatibility. type WorkerServer interface { - SetTestScript(context.Context, *SetTestScriptReq) (*Empty, error) + SetTestScript(context.Context, *TestScript) (*Empty, error) + GetTestScript(context.Context, *Empty) (*TestScript, error) Start(context.Context, *StartResp) (*Empty, error) Stop(context.Context, *Empty) (*Empty, error) ObserverChangeState(*Empty, grpc.ServerStreamingServer[StatusInfo]) error @@ -110,9 +123,12 @@ type WorkerServer interface { // pointer dereference when methods are called. type UnimplementedWorkerServer struct{} -func (UnimplementedWorkerServer) SetTestScript(context.Context, *SetTestScriptReq) (*Empty, error) { +func (UnimplementedWorkerServer) SetTestScript(context.Context, *TestScript) (*Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SetTestScript not implemented") } +func (UnimplementedWorkerServer) GetTestScript(context.Context, *Empty) (*TestScript, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTestScript not implemented") +} func (UnimplementedWorkerServer) Start(context.Context, *StartResp) (*Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Start not implemented") } @@ -144,7 +160,7 @@ func RegisterWorkerServer(s grpc.ServiceRegistrar, srv WorkerServer) { } func _Worker_SetTestScript_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetTestScriptReq) + in := new(TestScript) if err := dec(in); err != nil { return nil, err } @@ -156,7 +172,25 @@ func _Worker_SetTestScript_Handler(srv interface{}, ctx context.Context, dec fun FullMethod: Worker_SetTestScript_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(WorkerServer).SetTestScript(ctx, req.(*SetTestScriptReq)) + return srv.(WorkerServer).SetTestScript(ctx, req.(*TestScript)) + } + return interceptor(ctx, in, info, handler) +} + +func _Worker_GetTestScript_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WorkerServer).GetTestScript(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Worker_GetTestScript_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WorkerServer).GetTestScript(ctx, req.(*Empty)) } return interceptor(ctx, in, info, handler) } @@ -219,6 +253,10 @@ var Worker_ServiceDesc = grpc.ServiceDesc{ MethodName: "SetTestScript", Handler: _Worker_SetTestScript_Handler, }, + { + MethodName: "GetTestScript", + Handler: _Worker_GetTestScript_Handler, + }, { MethodName: "Start", Handler: _Worker_Start_Handler, diff --git a/worker/proto/worker.proto b/worker/proto/worker.proto index eaf7e66..20320a3 100644 --- a/worker/proto/worker.proto +++ b/worker/proto/worker.proto @@ -3,7 +3,8 @@ syntax = "proto3"; option go_package = "./gen"; service Worker { - rpc SetTestScript(SetTestScriptReq) returns(Empty) {} + rpc SetTestScript(TestScript) returns(Empty) {} + rpc GetTestScript(Empty) returns(TestScript) {} rpc Start(StartResp) returns(Empty) {} rpc Stop(Empty) returns(Empty) {} rpc ObserverChangeState(Empty) returns(stream StatusInfo) {} @@ -17,7 +18,7 @@ message StartResp { int32 test_count = 4; } -message SetTestScriptReq { +message TestScript { string script = 1; }