Skip to content

Commit bfd55cc

Browse files
Add test cases for AzureCLI tasks (#21761)
* Add comprehensive test cases for AzureCLIV1 - Bump version in task.json and task.loc.json to 1.269.0 - Introduce new test cases for various scenarios including: - Account set failure - Adding Service Principal to environment with Service Principal authentication - Adding Service Principal to environment with Workload Identity Federation - Handling Azure CLI version failure - Failing on standard error output - Inline script execution with temporary file - Login and logout failures - Managed Service Identity authentication - Service Principal authentication using certificate and key - Windows script path handling - Workload Identity Federation authentication * Add unit tests for Azure CLI task - Implemented tests for handling standard error output in FailOnStandardError_StderrPresent.ts. - Added KeepAzSessionActive_WIF.ts to test session refresh functionality with Workload Identity Federation. - Created ManagedServiceIdentity_Login.ts to validate Managed Service Identity authentication. - Developed ServicePrincipalCertificate_Login.ts for testing service principal key-based authentication. - Updated L0.ts to include new test cases for service principal authentication, managed identity, SPN environment variables, error handling, and session management. - Updated package-lock.json to reflect dependency updates and ensure compatibility.
1 parent e1f0c59 commit bfd55cc

26 files changed

+2208
-216
lines changed

Tasks/AzureCLIV1/Tests/L0.ts

Lines changed: 178 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,188 @@
1-
import fs = require('fs');
21
import assert = require('assert');
32
import path = require('path');
3+
import os = require('os');
4+
5+
import * as ttm from 'azure-pipelines-task-lib/mock-test';
46

57
describe('AzureCLIV1 Suite', function () {
6-
before(() => {
8+
this.timeout(20000);
9+
10+
// Use cross-platform temp directory for assertions
11+
const tempDir = require('os').tmpdir();
12+
13+
it('Service principal login runs script', async () => {
14+
const tp = path.join(__dirname, 'L0ServicePrincipalKey.js');
15+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
16+
17+
await tr.runAsync();
18+
19+
if (!tr.succeeded) {
20+
console.log('STDOUT:', tr.stdout);
21+
console.log('STDERR:', tr.stderr);
22+
}
23+
24+
assert(tr.succeeded, 'task should have succeeded');
25+
assert(tr.ran('/bin/bash /tmp/test.sh arg1 arg2'), 'should run script with args');
26+
assert(tr.ran('az login --service-principal -u "spId" --password="spKey" --tenant "tenantId"'), 'should login using service principal key');
27+
assert(tr.ran('az account set --subscription "subId"'), 'should set subscription');
728
});
829

9-
after(() => {
30+
it('Service principal certificate login runs script', async () => {
31+
const tp = path.join(__dirname, 'L0ServicePrincipalCertificate.js');
32+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
33+
34+
await tr.runAsync();
35+
36+
if (!tr.succeeded) {
37+
console.log('STDOUT:', tr.stdout);
38+
console.log('STDERR:', tr.stderr);
39+
}
40+
41+
assert(tr.succeeded, 'task should have succeeded');
42+
const certPath = path.join(tempDir, 'spnCert.pem');
43+
assert(tr.ran(`az login --service-principal -u "spId" --password="${certPath}" --tenant "tenantId"`), 'should login using service principal certificate');
44+
});
45+
46+
it('Inline script uses temp file and sets config dir', async () => {
47+
const tp = path.join(__dirname, 'L0InlineScriptTempFile.js');
48+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
49+
50+
await tr.runAsync();
51+
52+
if (!tr.succeeded) {
53+
console.log('STDOUT:', tr.stdout);
54+
console.log('STDERR:', tr.stderr);
55+
}
56+
57+
assert(tr.succeeded, 'task should have succeeded');
58+
const configDir = path.join(tempDir, '.azclitask');
59+
assert(tr.stdout.indexOf(`AZURE_CONFIG_DIR=${configDir}`) >= 0, 'should set AZURE_CONFIG_DIR');
60+
});
61+
62+
it('Managed identity login runs script', async () => {
63+
const tp = path.join(__dirname, 'L0ManagedServiceIdentity.js');
64+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
65+
66+
await tr.runAsync();
67+
68+
if (!tr.succeeded) {
69+
console.log('STDOUT:', tr.stdout);
70+
console.log('STDERR:', tr.stderr);
71+
}
72+
73+
assert(tr.succeeded, 'task should have succeeded');
74+
assert(tr.ran('az login --identity'), 'should login using managed identity');
75+
assert(tr.ran('/bin/bash /tmp/test.sh'), 'should run script');
76+
});
77+
78+
it('Workload identity federation login runs script', async () => {
79+
const tp = path.join(__dirname, 'L0WorkloadIdentityFederation.js');
80+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
81+
82+
await tr.runAsync();
83+
84+
if (!tr.succeeded) {
85+
console.log('STDOUT:', tr.stdout);
86+
console.log('STDERR:', tr.stderr);
87+
}
88+
89+
assert(tr.succeeded, 'task should have succeeded');
90+
assert(tr.ran('az login --service-principal -u "spId" --tenant "tenantId" --allow-no-subscriptions --federated-token "federatedToken"'), 'should login using federated token');
91+
});
92+
93+
it('Add SPN to environment (service principal) runs script', async () => {
94+
const tp = path.join(__dirname, 'L0AddSpnToEnvironment_ServicePrincipal.js');
95+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
96+
97+
await tr.runAsync();
98+
99+
if (!tr.succeeded) {
100+
console.log('STDOUT:', tr.stdout);
101+
console.log('STDERR:', tr.stderr);
102+
}
103+
104+
assert(tr.succeeded, 'task should have succeeded');
105+
assert(tr.ran('/bin/bash /tmp/test.sh'), 'should run script');
10106
});
11107

12-
it('Does a basic hello world test', function(done: MochaDone) {
13-
// TODO - add real tests
14-
done();
108+
it('Add SPN to environment (workload identity) runs script', async () => {
109+
const tp = path.join(__dirname, 'L0AddSpnToEnvironment_WorkloadIdentity.js');
110+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
111+
112+
await tr.runAsync();
113+
114+
if (!tr.succeeded) {
115+
console.log('STDOUT:', tr.stdout);
116+
console.log('STDERR:', tr.stderr);
117+
}
118+
119+
assert(tr.succeeded, 'task should have succeeded');
120+
assert(tr.ran('/bin/bash /tmp/test.sh'), 'should run script');
121+
});
122+
123+
if (os.platform() === 'win32') {
124+
it('Windows scriptPath uses script tool', async () => {
125+
const tp = path.join(__dirname, 'L0WindowsScriptPath.js');
126+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
127+
128+
await tr.runAsync();
129+
130+
if (!tr.succeeded) {
131+
console.log('STDOUT:', tr.stdout);
132+
console.log('STDERR:', tr.stderr);
133+
}
134+
135+
assert(tr.succeeded, 'task should have succeeded');
136+
});
137+
}
138+
139+
it('Fails when az login returns error', async () => {
140+
const tp = path.join(__dirname, 'L0LoginFailure.js');
141+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
142+
143+
await tr.runAsync();
144+
145+
assert(tr.failed, 'task should have failed');
146+
});
147+
148+
it('Fails when az --version returns error', async () => {
149+
const tp = path.join(__dirname, 'L0AzVersionFailure.js');
150+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
151+
152+
await tr.runAsync();
153+
154+
assert(tr.failed, 'task should have failed');
155+
});
156+
157+
it('Fails when account set returns error', async () => {
158+
const tp = path.join(__dirname, 'L0AccountSetFailure.js');
159+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
160+
161+
await tr.runAsync();
162+
163+
assert(tr.failed, 'task should have failed');
164+
});
165+
166+
it('Fails on stderr when failOnStandardError is true', async () => {
167+
const tp = path.join(__dirname, 'L0FailOnStdErr.js');
168+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
169+
170+
await tr.runAsync();
171+
172+
assert(tr.failed, 'task should have failed');
173+
});
174+
175+
it('Logout failure does not fail task', async () => {
176+
const tp = path.join(__dirname, 'L0LogoutFailure.js');
177+
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
178+
179+
await tr.runAsync();
180+
181+
if (!tr.succeeded) {
182+
console.log('STDOUT:', tr.stdout);
183+
console.log('STDERR:', tr.stderr);
184+
}
185+
186+
assert(tr.succeeded, 'task should have succeeded');
15187
});
16188
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import ma = require('azure-pipelines-task-lib/mock-answer');
2+
import tmrm = require('azure-pipelines-task-lib/mock-run');
3+
import path = require('path');
4+
import os = require('os');
5+
6+
const taskPath = path.join(__dirname, '..', 'azureclitask.js');
7+
const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
8+
9+
// Use cross-platform temp directory
10+
const tempDir = os.tmpdir();
11+
process.env['AGENT_TEMPDIRECTORY'] = tempDir;
12+
13+
// Inputs
14+
tmr.setInput('connectedServiceNameARM', 'AzureRM');
15+
tmr.setInput('scriptLocation', 'scriptPath');
16+
tmr.setInput('scriptPath', '/tmp/test.sh');
17+
tmr.setInput('cwd', '/tmp');
18+
tmr.setInput('args', '');
19+
tmr.setInput('useGlobalConfig', 'true');
20+
21+
// Mock Endpoint
22+
process.env['ENDPOINT_AUTH_SCHEME_AzureRM'] = 'ServicePrincipal';
23+
process.env['ENDPOINT_AUTH_PARAMETER_AzureRM_AUTHENTICATIONTYPE'] = 'spnKey';
24+
process.env['ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALID'] = 'spId';
25+
process.env['ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY'] = 'spKey';
26+
process.env['ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID'] = 'tenantId';
27+
process.env['ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID'] = 'subId';
28+
process.env['ENDPOINT_DATA_AzureRM_ENVIRONMENT'] = 'AzureCloud';
29+
30+
const a: ma.TaskLibAnswers = <ma.TaskLibAnswers>{
31+
which: {
32+
az: 'az',
33+
bash: '/bin/bash'
34+
},
35+
checkPath: {
36+
'/tmp': true,
37+
'/tmp/test.sh': true,
38+
'/bin/bash': true,
39+
'az': true
40+
},
41+
exec: {
42+
'az --version': {
43+
code: 0,
44+
stdout: 'azure-cli 2.0.0'
45+
},
46+
'az cloud set -n AzureCloud': {
47+
code: 0,
48+
stdout: 'cloud set'
49+
},
50+
'az login --service-principal -u "spId" --password="spKey" --tenant "tenantId"': {
51+
code: 0,
52+
stdout: 'login ok'
53+
},
54+
'az account set --subscription "subId"': {
55+
code: 1,
56+
stderr: 'account set failed'
57+
}
58+
}
59+
};
60+
61+
tmr.setAnswers(a);
62+
63+
os.type = () => 'Linux';
64+
tmr.registerMock('os', os);
65+
tmr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner'));
66+
67+
tmr.run();
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import ma = require('azure-pipelines-task-lib/mock-answer');
2+
import tmrm = require('azure-pipelines-task-lib/mock-run');
3+
import path = require('path');
4+
import os = require('os');
5+
6+
const taskPath = path.join(__dirname, '..', 'azureclitask.js');
7+
const tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
8+
9+
// Use cross-platform temp directory
10+
const tempDir = os.tmpdir();
11+
process.env['AGENT_TEMPDIRECTORY'] = tempDir;
12+
13+
// Inputs
14+
tmr.setInput('connectedServiceNameARM', 'AzureRM');
15+
tmr.setInput('scriptLocation', 'scriptPath');
16+
tmr.setInput('scriptPath', '/tmp/test.sh');
17+
tmr.setInput('cwd', '/tmp');
18+
tmr.setInput('args', '');
19+
tmr.setInput('useGlobalConfig', 'true');
20+
tmr.setInput('addSpnToEnvironment', 'true');
21+
22+
// Mock Endpoint
23+
process.env['ENDPOINT_AUTH_SCHEME_AzureRM'] = 'ServicePrincipal';
24+
process.env['ENDPOINT_AUTH_PARAMETER_AzureRM_AUTHENTICATIONTYPE'] = 'spnKey';
25+
process.env['ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALID'] = 'spId';
26+
process.env['ENDPOINT_AUTH_PARAMETER_AzureRM_SERVICEPRINCIPALKEY'] = 'spKey';
27+
process.env['ENDPOINT_AUTH_PARAMETER_AzureRM_TENANTID'] = 'tenantId';
28+
process.env['ENDPOINT_DATA_AzureRM_SUBSCRIPTIONID'] = 'subId';
29+
process.env['ENDPOINT_DATA_AzureRM_ENVIRONMENT'] = 'AzureCloud';
30+
31+
const a: ma.TaskLibAnswers = <ma.TaskLibAnswers>{
32+
which: {
33+
az: 'az',
34+
bash: '/bin/bash'
35+
},
36+
checkPath: {
37+
'/tmp': true,
38+
'/tmp/test.sh': true,
39+
'/bin/bash': true,
40+
'az': true
41+
},
42+
exec: {
43+
'az --version': {
44+
code: 0,
45+
stdout: 'azure-cli 2.0.0'
46+
},
47+
'az cloud set -n AzureCloud': {
48+
code: 0,
49+
stdout: 'cloud set'
50+
},
51+
'az login --service-principal -u "spId" --password="spKey" --tenant "tenantId"': {
52+
code: 0,
53+
stdout: 'login ok'
54+
},
55+
'az account set --subscription "subId"': {
56+
code: 0,
57+
stdout: 'subscription set'
58+
},
59+
'az account clear': {
60+
code: 0,
61+
stdout: 'account cleared'
62+
},
63+
'/bin/bash /tmp/test.sh': {
64+
code: 0,
65+
stdout: 'script ok'
66+
}
67+
}
68+
};
69+
70+
tmr.setAnswers(a);
71+
72+
os.type = () => 'Linux';
73+
tmr.registerMock('os', os);
74+
tmr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner'));
75+
76+
tmr.run();

0 commit comments

Comments
 (0)