• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

UiPath / uipathcli / 13948669907

19 Mar 2025 02:05PM UTC coverage: 90.475% (+0.004%) from 90.471%
13948669907

Pull #167

github

thschmitt
Add support for authenticated library feeds

Use the provided auth token from the configured identity client
to authenticate with the connected Orchestrator when restoring packages
so that library packages can be retrieved from authenticated feeds.

Extended the `uipath studio package pack` and `uipath studio test run`
commands to use the existing auth token for authentication with the
Orchestrator feeds.

Implementation details:

- Using the latest uipcli which supports JWT bearer tokens and passing
  the short-lived token to the uipcli.

- Reduced the boilerplate in the studio plugin tests.

Issue: https://github.com/UiPath/uipathcli/issues/149
Pull Request #167: Add support for authenticated library feeds

54 of 60 new or added lines in 3 files covered. (90.0%)

2 existing lines in 1 file now uncovered.

5946 of 6572 relevant lines covered (90.47%)

1.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.05
/plugin/studio/test_run_command.go
1
package studio
2

3
import (
4
        "bytes"
5
        "encoding/json"
6
        "fmt"
7
        "net/url"
8
        "os"
9
        "path/filepath"
10
        "strings"
11
        "time"
12

13
        "github.com/UiPath/uipathcli/log"
14
        "github.com/UiPath/uipathcli/output"
15
        "github.com/UiPath/uipathcli/plugin"
16
        "github.com/UiPath/uipathcli/utils/api"
17
        "github.com/UiPath/uipathcli/utils/directories"
18
        "github.com/UiPath/uipathcli/utils/process"
19
        "github.com/UiPath/uipathcli/utils/stream"
20
        "github.com/UiPath/uipathcli/utils/visualization"
21
)
22

23
// The TestRunCommand packs a project as a test package,
24
// uploads it to the connected Orchestrator instances
25
// and runs the tests.
26
type TestRunCommand struct {
27
        Exec process.ExecProcess
28
}
29

30
func (c TestRunCommand) Command() plugin.Command {
1✔
31
        return *plugin.NewCommand("studio").
1✔
32
                WithCategory("test", "Test", "Tests your UiPath studio packages").
1✔
33
                WithOperation("run", "Run Tests", "Tests a given package").
1✔
34
                WithParameter("source", plugin.ParameterTypeString, "Path to a project.json file or a folder containing project.json file (default: .)", false).
1✔
35
                WithParameter("timeout", plugin.ParameterTypeInteger, "Time to wait in seconds for tests to finish (default: 3600)", false)
1✔
36
}
1✔
37

38
func (c TestRunCommand) Execute(ctx plugin.ExecutionContext, writer output.OutputWriter, logger log.Logger) error {
1✔
39
        source, err := c.getSource(ctx)
1✔
40
        if err != nil {
2✔
41
                return err
1✔
42
        }
1✔
43
        timeout := time.Duration(c.getIntParameter("timeout", 3600, ctx.Parameters)) * time.Second
1✔
44
        result, err := c.execute(source, timeout, ctx, logger)
1✔
45
        if err != nil {
2✔
46
                return err
1✔
47
        }
1✔
48

49
        json, err := json.Marshal(result)
1✔
50
        if err != nil {
1✔
51
                return fmt.Errorf("pack command failed: %v", err)
×
52
        }
×
53
        return writer.WriteResponse(*output.NewResponseInfo(200, "200 OK", "HTTP/1.1", map[string][]string{}, bytes.NewReader(json)))
1✔
54
}
55

56
func (c TestRunCommand) execute(source string, timeout time.Duration, ctx plugin.ExecutionContext, logger log.Logger) (*testRunResult, error) {
1✔
57
        projectReader := newStudioProjectReader(source)
1✔
58
        project, err := projectReader.ReadMetadata()
1✔
59
        if err != nil {
1✔
60
                return nil, err
×
61
        }
×
62
        supported, err := project.TargetFramework.IsSupported()
1✔
63
        if !supported {
1✔
64
                return nil, err
×
65
        }
×
66
        tmp, err := directories.Temp()
1✔
67
        if err != nil {
1✔
68
                return nil, err
×
69
        }
×
70
        destination := filepath.Join(tmp, ctx.Settings.OperationId)
1✔
71
        defer os.RemoveAll(destination)
1✔
72

1✔
73
        uipcli := newUipcli(c.Exec, logger)
1✔
74
        err = uipcli.Initialize(project.TargetFramework)
1✔
75
        if err != nil {
1✔
76
                return nil, err
×
77
        }
×
78

79
        packParams := newPackagePackParams(
1✔
80
                ctx.Organization,
1✔
81
                ctx.Tenant,
1✔
82
                ctx.BaseUri,
1✔
83
                ctx.Auth.Token,
1✔
84
                source,
1✔
85
                destination,
1✔
86
                "",
1✔
87
                true,
1✔
88
                "Tests",
1✔
89
                false,
1✔
90
                "")
1✔
91
        exitCode, stdErr, err := c.executeUipcli(uipcli, *packParams, ctx.Debug, logger)
1✔
92
        if err != nil {
1✔
93
                return nil, err
×
94
        }
×
95
        if exitCode != 0 {
1✔
96
                return nil, fmt.Errorf("Error packaging tests: %v", stdErr)
×
97
        }
×
98

99
        nupkgPath := findLatestNupkg(destination)
1✔
100
        nupkgReader := newNupkgReader(nupkgPath)
1✔
101
        nuspec, err := nupkgReader.ReadNuspec()
1✔
102
        if err != nil {
2✔
103
                return nil, err
1✔
104
        }
1✔
105

106
        params := newTestRunParams(nupkgPath, nuspec.Id, nuspec.Version, timeout)
1✔
107
        execution, err := c.runTests(*params, ctx, logger)
1✔
108
        if err != nil {
2✔
109
                return nil, err
1✔
110
        }
1✔
111
        return newTestRunResult(*execution), nil
1✔
112
}
113

114
func (c TestRunCommand) runTests(params testRunParams, ctx plugin.ExecutionContext, logger log.Logger) (*api.TestExecution, error) {
1✔
115
        progressBar := visualization.NewProgressBar(logger)
1✔
116
        defer progressBar.Remove()
1✔
117
        progressBar.UpdatePercentage("uploading...", 0)
1✔
118

1✔
119
        baseUri := c.formatUri(ctx.BaseUri, ctx.Organization, ctx.Tenant)
1✔
120
        client := api.NewOrchestratorClient(baseUri, ctx.Auth.Token, ctx.Debug, ctx.Settings, logger)
1✔
121
        folderId, err := client.GetSharedFolderId()
1✔
122
        if err != nil {
2✔
123
                return nil, err
1✔
124
        }
1✔
125
        file := stream.NewFileStream(params.NupkgPath)
1✔
126
        err = client.Upload(file, progressBar)
1✔
127
        if err != nil {
2✔
128
                return nil, err
1✔
129
        }
1✔
130
        releaseId, err := client.CreateOrUpdateRelease(folderId, params.ProcessKey, params.ProcessVersion)
1✔
131
        if err != nil {
2✔
132
                return nil, err
1✔
133
        }
1✔
134
        testSetId, err := client.CreateTestSet(folderId, releaseId, params.ProcessVersion)
1✔
135
        if err != nil {
2✔
136
                return nil, err
1✔
137
        }
1✔
138
        executionId, err := client.ExecuteTestSet(folderId, testSetId)
1✔
139
        if err != nil {
2✔
140
                return nil, err
1✔
141
        }
1✔
142
        return client.WaitForTestExecutionToFinish(folderId, executionId, params.Timeout, func(execution api.TestExecution) {
2✔
143
                total := len(execution.TestCaseExecutions)
1✔
144
                completed := 0
1✔
145
                for _, testCase := range execution.TestCaseExecutions {
2✔
146
                        if testCase.IsCompleted() {
2✔
147
                                completed++
1✔
148
                        }
1✔
149
                }
150
                progressBar.UpdateSteps("running...  ", completed, total)
1✔
151
        })
152
}
153

154
func (c TestRunCommand) executeUipcli(uipcli *uipcli, params packagePackParams, debug bool, logger log.Logger) (int, string, error) {
1✔
155
        if !debug {
2✔
156
                bar := c.newPackagingProgressBar(logger)
1✔
157
                defer close(bar)
1✔
158
        }
1✔
159
        args := c.preparePackArguments(params)
1✔
160
        return uipcli.ExecuteAndWait(args...)
1✔
161
}
162

163
func (c TestRunCommand) preparePackArguments(params packagePackParams) []string {
1✔
164
        args := []string{"package", "pack", params.Source, "--output", params.Destination}
1✔
165
        if params.PackageVersion != "" {
1✔
NEW
166
                args = append(args, "--version", params.PackageVersion)
×
NEW
167
        }
×
168
        if params.AutoVersion {
2✔
169
                args = append(args, "--autoVersion")
1✔
170
        }
1✔
171
        if params.OutputType != "" {
2✔
172
                args = append(args, "--outputType", params.OutputType)
1✔
173
        }
1✔
174
        if params.SplitOutput {
1✔
NEW
175
                args = append(args, "--splitOutput")
×
NEW
176
        }
×
177
        if params.ReleaseNotes != "" {
1✔
NEW
178
                args = append(args, "--releaseNotes", params.ReleaseNotes)
×
NEW
179
        }
×
180
        if params.AuthToken != nil && params.Organization != "" {
2✔
181
                args = append(args, "--libraryOrchestratorUrl", params.BaseUri.String())
1✔
182
                args = append(args, "--libraryOrchestratorAuthToken", params.AuthToken.Value)
1✔
183
                args = append(args, "--libraryOrchestratorAccountName", params.Organization)
1✔
184
                if params.Tenant != "" {
2✔
185
                        args = append(args, "--libraryOrchestratorTenant", params.Tenant)
1✔
186
                }
1✔
187
        }
188
        return args
1✔
189
}
190

191
func (c TestRunCommand) newPackagingProgressBar(logger log.Logger) chan struct{} {
1✔
192
        progressBar := visualization.NewProgressBar(logger)
1✔
193
        ticker := time.NewTicker(10 * time.Millisecond)
1✔
194
        cancel := make(chan struct{})
1✔
195
        var percent float64 = 0
1✔
196
        go func() {
2✔
197
                for {
2✔
198
                        select {
1✔
199
                        case <-ticker.C:
1✔
200
                                progressBar.UpdatePercentage("packaging...  ", percent)
1✔
201
                                percent = percent + 1
1✔
202
                                if percent > 100 {
2✔
203
                                        percent = 0
1✔
204
                                }
1✔
205
                        case <-cancel:
1✔
206
                                ticker.Stop()
1✔
207
                                progressBar.Remove()
1✔
208
                                return
1✔
209
                        }
210
                }
211
        }()
212
        return cancel
1✔
213
}
214

215
func (c TestRunCommand) getSource(ctx plugin.ExecutionContext) (string, error) {
1✔
216
        source := c.getParameter("source", ".", ctx.Parameters)
1✔
217
        source, _ = filepath.Abs(source)
1✔
218
        fileInfo, err := os.Stat(source)
1✔
219
        if err != nil {
2✔
220
                return "", fmt.Errorf("%s not found", defaultProjectJson)
1✔
221
        }
1✔
222
        if fileInfo.IsDir() {
2✔
223
                source = filepath.Join(source, defaultProjectJson)
1✔
224
        }
1✔
225
        return source, nil
1✔
226
}
227

228
func (c TestRunCommand) getIntParameter(name string, defaultValue int, parameters []plugin.ExecutionParameter) int {
1✔
229
        result := defaultValue
1✔
230
        for _, p := range parameters {
2✔
231
                if p.Name == name {
2✔
232
                        if data, ok := p.Value.(int); ok {
2✔
233
                                result = data
1✔
234
                                break
1✔
235
                        }
236
                }
237
        }
238
        return result
1✔
239
}
240

241
func (c TestRunCommand) getParameter(name string, defaultValue string, parameters []plugin.ExecutionParameter) string {
1✔
242
        result := defaultValue
1✔
243
        for _, p := range parameters {
2✔
244
                if p.Name == name {
2✔
245
                        if data, ok := p.Value.(string); ok {
2✔
246
                                result = data
1✔
247
                                break
1✔
248
                        }
249
                }
250
        }
251
        return result
1✔
252
}
253

254
func (c TestRunCommand) formatUri(baseUri url.URL, org string, tenant string) string {
1✔
255
        path := baseUri.Path
1✔
256
        if baseUri.Path == "" {
2✔
257
                path = "/{organization}/{tenant}/orchestrator_"
1✔
258
        }
1✔
259
        path = strings.ReplaceAll(path, "{organization}", org)
1✔
260
        path = strings.ReplaceAll(path, "{tenant}", tenant)
1✔
261
        path = strings.TrimSuffix(path, "/")
1✔
262
        return fmt.Sprintf("%s://%s%s", baseUri.Scheme, baseUri.Host, path)
1✔
263
}
264

265
func NewTestRunCommand() *TestRunCommand {
1✔
266
        return &TestRunCommand{process.NewExecProcess()}
1✔
267
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc