Added L0 tests. (#22)
* Added L0 tests. * Updated according to review comments.
This commit is contained in:
parent
f79ce17439
commit
56b95ecccc
81
.github/workflows/test.yml
vendored
81
.github/workflows/test.yml
vendored
@ -1,80 +1,21 @@
|
|||||||
on:
|
name: "build-test"
|
||||||
|
on: # rebuild any PRs and main branch changes
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- 'releases/*'
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- 'releases/*'
|
- 'releases/*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_test_job:
|
build: # make sure build/ci works properly
|
||||||
name: 'Build and test job'
|
runs-on: ubuntu-latest
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
- name: 'Checking out repo code'
|
- name: build and run tests
|
||||||
uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Extract branch name
|
|
||||||
id: extract_branch
|
|
||||||
run: |
|
|
||||||
echo "##[set-output name=branchname;]$(echo ${GITHUB_REF##*/})"
|
|
||||||
|
|
||||||
- name: 'Install dependency for master'
|
|
||||||
if: github.event.pull_request.base.ref == 'master' || steps.extract_branch.outputs.branchname == 'master'
|
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
|
npm build
|
||||||
- name: 'Install dependency for releases'
|
npm test
|
||||||
if: github.event.pull_request.base.ref == 'releases/v1' || steps.extract_branch.outputs.branchname == 'releases/v1'
|
|
||||||
run: |
|
|
||||||
npm install --only=dev
|
|
||||||
|
|
||||||
- name: 'Run L0 tests'
|
|
||||||
run: |
|
|
||||||
npm run test
|
|
||||||
- name : 'Run test coverage'
|
|
||||||
if: runner.os == 'Windows' && (github.event.pull_request.base.ref == 'releases/v1' || github.event.pull_request.base.ref == 'master')
|
|
||||||
env:
|
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
$coverage_result = npm run test-coverage
|
|
||||||
$start = $false;
|
|
||||||
$middle = $false;
|
|
||||||
$end = $false;
|
|
||||||
$count = 0;
|
|
||||||
|
|
||||||
foreach ($j in $coverage_result)
|
|
||||||
{
|
|
||||||
if ($j.tostring().startswith("----------"))
|
|
||||||
{
|
|
||||||
if (!$start)
|
|
||||||
{
|
|
||||||
$start = $true;
|
|
||||||
$start_index = $count
|
|
||||||
}
|
|
||||||
elseif (!$middle)
|
|
||||||
{
|
|
||||||
$middle = $true;
|
|
||||||
}
|
|
||||||
elseif (!$end)
|
|
||||||
{
|
|
||||||
$end = $true;
|
|
||||||
$end_index = $count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$count++
|
|
||||||
}
|
|
||||||
|
|
||||||
$tbl_md = $coverage_result[($start_index+1)..($end_index-1)] -join "\n"
|
|
||||||
$summary = $coverage_result[($end_index + 1)..$count] -join "\n"
|
|
||||||
$comment = $tbl_md + "\n" + $summary
|
|
||||||
$url = "https://api.github.com/repos/${env:GITHUB_REPOSITORY}/issues/${env:PR_NUMBER}/comments"
|
|
||||||
$headers = @{
|
|
||||||
"Authorization" = "token ${env:GITHUB_TOKEN}"
|
|
||||||
}
|
|
||||||
$body = "{ `"body`": `"${comment}`" }"
|
|
||||||
Invoke-RestMethod -Method POST -Uri $url -Headers $headers -Body $body
|
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -328,3 +328,4 @@ ASALocalRun/
|
|||||||
# MFractors (Xamarin productivity tool) working folder
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
.mfractor/
|
.mfractor/
|
||||||
node_modules
|
node_modules
|
||||||
|
coverage
|
||||||
|
@ -1,7 +1,199 @@
|
|||||||
import { run } from '../src/login'
|
import * as run from '../src/login'
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
import * as toolCache from '@actions/tool-cache';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as jsyaml from 'js-yaml';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
describe('This is a placeholder for intial test cases, to be removed', () => {
|
var mockStatusCode;
|
||||||
test('Dummy test case', async () => {
|
const mockExecFn = jest.fn().mockImplementation(() => mockStatusCode);
|
||||||
await expect(run()).rejects.toThrow();
|
jest.mock('@actions/exec/lib/toolrunner', () => {
|
||||||
|
return {
|
||||||
|
ToolRunner: jest.fn().mockImplementation(() => {
|
||||||
|
return {
|
||||||
|
exec: mockExecFn
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('Testing all functions.', () => {
|
||||||
|
test('getExecutableExtension() - return .exe when os is Windows', () => {
|
||||||
|
jest.spyOn(os, 'type').mockReturnValue('Windows_NT');
|
||||||
|
|
||||||
|
expect(run.getExecutableExtension()).toBe('.exe');
|
||||||
|
expect(os.type).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getExecutableExtension() - return empty string for non-windows OS', () => {
|
||||||
|
jest.spyOn(os, 'type').mockReturnValue('Darwin');
|
||||||
|
|
||||||
|
expect(run.getExecutableExtension()).toBe('');
|
||||||
|
expect(os.type).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubectlPath() - return path to existing kubectl', async () => {
|
||||||
|
jest.spyOn(io, 'which').mockResolvedValue('pathToKubectl');
|
||||||
|
|
||||||
|
expect(await run.getKubectlPath()).toBe('pathToKubectl');
|
||||||
|
expect(io.which).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubectlPath() - throw error when kubectl not installed', async () => {
|
||||||
|
jest.spyOn(io, 'which').mockResolvedValue('');
|
||||||
|
jest.spyOn(toolCache, 'findAllVersions').mockReturnValue([]);
|
||||||
|
|
||||||
|
await expect(run.getKubectlPath()).rejects.toThrow('Kubectl is not installed');
|
||||||
|
expect(io.which).toBeCalled();
|
||||||
|
expect(toolCache.findAllVersions).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubectlPath() - return path to kubectl in toolCache', async () => {
|
||||||
|
jest.spyOn(io, 'which').mockResolvedValue('');
|
||||||
|
jest.spyOn(toolCache, 'findAllVersions').mockReturnValue(['v1.15.0']);
|
||||||
|
jest.spyOn(toolCache, 'find').mockReturnValue('pathToTool');
|
||||||
|
jest.spyOn(os, 'type').mockReturnValue('Windows_NT');
|
||||||
|
|
||||||
|
expect(await run.getKubectlPath()).toBe(path.join('pathToTool', 'kubectl.exe'));
|
||||||
|
expect(io.which).toBeCalled();
|
||||||
|
expect(toolCache.findAllVersions).toBeCalled();
|
||||||
|
expect(toolCache.find).toBeCalledWith('kubectl', 'v1.15.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubeconfig() - throw error on invalid input', () => {
|
||||||
|
jest.spyOn(core, 'getInput').mockReturnValue('invalid');
|
||||||
|
|
||||||
|
expect(() => run.getKubeconfig()).toThrow('Invalid method specified. Acceptable values are kubeconfig and service-account.');
|
||||||
|
expect(core.getInput).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubeconfig() - return kubeconfig from input', () => {
|
||||||
|
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||||
|
if (inputName == 'method') return 'kubeconfig';
|
||||||
|
if (inputName == 'kubeconfig') return '###';
|
||||||
|
});
|
||||||
|
jest.spyOn(core, 'debug').mockImplementation();
|
||||||
|
|
||||||
|
expect(run.getKubeconfig()).toBe('###');
|
||||||
|
expect(core.getInput).toBeCalledWith('method', { required: true });
|
||||||
|
expect(core.getInput).toBeCalledWith('kubeconfig', { required: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubeconfig() - create kubeconfig from secret provided and return it', () => {
|
||||||
|
jest.spyOn(core, 'debug').mockImplementation();
|
||||||
|
const k8Secret = fs.readFileSync('__tests__/sample-secret.yml').toString();
|
||||||
|
const kubeconfig = JSON.stringify({
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Config",
|
||||||
|
"clusters": [
|
||||||
|
{
|
||||||
|
"cluster": {
|
||||||
|
"certificate-authority-data": 'LS0tLS1CRUdJTiBDRWyUSUZJQ',
|
||||||
|
"server": 'https://testing-dns-4za.hfp.earth.azmk8s.io:443'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"user": {
|
||||||
|
"token": Buffer.from('ZXlKaGJHY2lPcUpTVXpJMU5pSX=', 'base64').toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||||
|
if (inputName == 'method') return 'service-account';
|
||||||
|
if (inputName == 'k8s-url') return 'https://testing-dns-4za.hfp.earth.azmk8s.io:443';
|
||||||
|
if (inputName == 'k8s-secret') return k8Secret;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(run.getKubeconfig()).toBe(kubeconfig);
|
||||||
|
expect(core.getInput).toBeCalledTimes(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubeconfig() - throw error if empty config provided', () => {
|
||||||
|
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||||
|
if (inputName == 'method') return 'service-account';
|
||||||
|
if (inputName == 'k8s-url') return 'https://testing-dns-4da.hcp.earth.azmk8s.io:443';
|
||||||
|
if (inputName == 'k8s-secret') return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => run.getKubeconfig()).toThrow('The service account secret yaml specified is invalid. Make sure that its a valid yaml and try again.');
|
||||||
|
expect(core.getInput).toBeCalledTimes(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubeconfig() - throw error if data field doesn\'t exist', () => {
|
||||||
|
var k8SecretYaml = fs.readFileSync('__tests__/sample-secret.yml').toString();
|
||||||
|
var k8SecretObject = jsyaml.safeLoad(k8SecretYaml);
|
||||||
|
delete k8SecretObject['data'];
|
||||||
|
k8SecretYaml = jsyaml.dump(k8SecretObject);
|
||||||
|
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||||
|
if (inputName == 'method') return 'service-account';
|
||||||
|
if (inputName == 'k8s-url') return 'https://testing-dns-4da.hcp.earth.azmk8s.io:443';
|
||||||
|
if (inputName == 'k8s-secret') return k8SecretYaml;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => run.getKubeconfig()).toThrow('The service account secret yaml does not contain data; field. Make sure that its present and try again.');
|
||||||
|
expect(core.getInput).toBeCalledTimes(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubeconfig() - throw error if data.token field doesn\'t exist', () => {
|
||||||
|
var k8SecretYaml = fs.readFileSync('__tests__/sample-secret.yml').toString();
|
||||||
|
var k8SecretObject = jsyaml.safeLoad(k8SecretYaml);
|
||||||
|
delete k8SecretObject['data']['token'];
|
||||||
|
k8SecretYaml = jsyaml.dump(k8SecretObject);
|
||||||
|
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||||
|
if (inputName == 'method') return 'service-account';
|
||||||
|
if (inputName == 'k8s-url') return 'https://testing-dns-4da.hcp.earth.azmk8s.io:443';
|
||||||
|
if (inputName == 'k8s-secret') return k8SecretYaml;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => run.getKubeconfig()).toThrow('The service account secret yaml does not contain data.token; field. Make sure that its present and try again.');
|
||||||
|
expect(core.getInput).toBeCalledTimes(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getKubeconfig() - throw error if data[ca.crt] field doesn\'t exist', () => {
|
||||||
|
var k8SecretYaml = fs.readFileSync('__tests__/sample-secret.yml').toString();
|
||||||
|
var k8SecretObject = jsyaml.safeLoad(k8SecretYaml);
|
||||||
|
delete k8SecretObject['data']['ca.crt'];
|
||||||
|
k8SecretYaml = jsyaml.dump(k8SecretObject);
|
||||||
|
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||||
|
if (inputName == 'method') return 'service-account';
|
||||||
|
if (inputName == 'k8s-url') return 'https://testing-dns-4da.hcp.earth.azmk8s.io:443';
|
||||||
|
if (inputName == 'k8s-secret') return k8SecretYaml;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => run.getKubeconfig()).toThrow('The service account secret yaml does not contain data[ca.crt]; field. Make sure that its present and try again.');
|
||||||
|
expect(core.getInput).toBeCalledTimes(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setContext() - set context using kubectl', async () => {
|
||||||
|
jest.spyOn(core, 'getInput').mockReturnValue('abc');
|
||||||
|
jest.spyOn(io, 'which').mockResolvedValue('pathToKubectl');
|
||||||
|
mockStatusCode = 0;
|
||||||
|
|
||||||
|
expect(await run.setContext('/pathToKubeconfig'));
|
||||||
|
expect(mockExecFn).toBeCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('run() - create kubeconfig, exportvariable and give appropriate access', async () => {
|
||||||
|
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
|
||||||
|
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {});
|
||||||
|
jest.spyOn(core, 'exportVariable').mockImplementation(() => {});
|
||||||
|
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||||
|
if (inputName == 'method') return 'kubeconfig';
|
||||||
|
if (inputName == 'kubeconfig') return '###';
|
||||||
|
if (inputName == 'context') return '';
|
||||||
|
});
|
||||||
|
process.env['RUNNER_TEMP'] = 'tempDirPath'
|
||||||
|
jest.spyOn(Date, 'now').mockImplementation(() => 1234561234567);
|
||||||
|
|
||||||
|
expect(run.run());
|
||||||
|
expect(fs.writeFileSync).toHaveBeenCalledWith(path.join('tempDirPath', 'kubeconfig_1234561234567'), '###');
|
||||||
|
expect(fs.chmodSync).toHaveBeenCalledWith(path.join('tempDirPath', 'kubeconfig_1234561234567'), '600');
|
||||||
|
expect(core.exportVariable).toHaveBeenCalledWith('KUBECONFIG', path.join('tempDirPath', 'kubeconfig_1234561234567'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
35
__tests__/sample-secret.yml
Normal file
35
__tests__/sample-secret.yml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
ca.crt: LS0tLS1CRUdJTiBDRWyUSUZJQ
|
||||||
|
namespace: ZGVmBXUsLdA==
|
||||||
|
token: ZXlKaGJHY2lPcUpTVXpJMU5pSX=
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/service-account.name: default
|
||||||
|
kubernetes.io/service-account.uid: e1414a3z-22fe-48d1-ab9e-18e4a5b91c
|
||||||
|
creationTimestamp: "2020-03-02T06:40:31Z"
|
||||||
|
managedFields:
|
||||||
|
- apiVersion: v1
|
||||||
|
fieldsType: FieldsV1
|
||||||
|
fieldsV1:
|
||||||
|
f:data:
|
||||||
|
.: {}
|
||||||
|
f:ca.crt: {}
|
||||||
|
f:namespace: {}
|
||||||
|
f:token: {}
|
||||||
|
f:metadata:
|
||||||
|
f:annotations:
|
||||||
|
.: {}
|
||||||
|
f:kubernetes.io/service-account.name: {}
|
||||||
|
f:kubernetes.io/service-account.uid: {}
|
||||||
|
f:type: {}
|
||||||
|
manager: kube-controller-manager
|
||||||
|
operation: Update
|
||||||
|
time: "2020-03-02T06:40:31Z"
|
||||||
|
name: default-token-bl8ra
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: "278"
|
||||||
|
selfLink: /api/v1/namespaces/default/secrets/default-token-bl8ra
|
||||||
|
uid: e6d8b21b-2e3a-4606-98za-54fb44fdc
|
||||||
|
type: kubernetes.io/service-account-token
|
@ -8,7 +8,7 @@ import { ToolRunner } from "@actions/exec/lib/toolrunner";
|
|||||||
import * as jsyaml from 'js-yaml';
|
import * as jsyaml from 'js-yaml';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
|
|
||||||
function getKubeconfig(): string {
|
export function getKubeconfig(): string {
|
||||||
const method = core.getInput('method', {required: true});
|
const method = core.getInput('method', {required: true});
|
||||||
if (method == 'kubeconfig') {
|
if (method == 'kubeconfig') {
|
||||||
const kubeconfig = core.getInput('kubeconfig', {required : true});
|
const kubeconfig = core.getInput('kubeconfig', {required : true});
|
||||||
@ -66,7 +66,7 @@ function getKubeconfig(): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExecutableExtension(): string {
|
export function getExecutableExtension(): string {
|
||||||
if (os.type().match(/^Win/)) {
|
if (os.type().match(/^Win/)) {
|
||||||
return '.exe';
|
return '.exe';
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ function getExecutableExtension(): string {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getKubectlPath() {
|
export async function getKubectlPath() {
|
||||||
let kubectlPath = await io.which('kubectl', false);
|
let kubectlPath = await io.which('kubectl', false);
|
||||||
if (!kubectlPath) {
|
if (!kubectlPath) {
|
||||||
const allVersions = toolCache.findAllVersions('kubectl');
|
const allVersions = toolCache.findAllVersions('kubectl');
|
||||||
@ -88,7 +88,7 @@ async function getKubectlPath() {
|
|||||||
return kubectlPath;
|
return kubectlPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setContext(kubeconfigPath: string) {
|
export async function setContext(kubeconfigPath: string) {
|
||||||
let context = core.getInput('context');
|
let context = core.getInput('context');
|
||||||
if (context) {
|
if (context) {
|
||||||
//To use kubectl commands, the environment variable KUBECONFIG needs to be set for this step
|
//To use kubectl commands, the environment variable KUBECONFIG needs to be set for this step
|
||||||
|
Loading…
x
Reference in New Issue
Block a user