Skip to content

Complete test coverage #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/acceptance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: acceptance

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

jobs:
acceptance:
runs-on: ubuntu-latest
steps:
# Need to checkout for testing the Action in this repo
- uses: actions/checkout@v4
with:
fetch-depth: 0 # needed to checkout all branches

# Start check the PR diff
- uses: ./
id: git-diff-action
with:
git_diff_file: __tests__/fixtures/main.diff
json_diff_file_output: diff.json
raw_diff_file_output: diff.txt

# Print the diff in JSON format
- name: print json diff
env:
DIFF: ${{ steps.git-diff-action.outputs.json-diff-path }}
run: cat $DIFF

# Echo the raw git diff
- name: print raw diff
env:
DIFF: ${{ steps.git-diff-action.outputs.raw-diff-path }}
run: cat $DIFF

- name: verify checksums
run: |
result_checksum=$(shasum -a 256 diff.json)
expected_checksum=$(cat __tests__/fixtures/diff.json.sha256)
echo "..result_checksum: $result_checksum"
echo "expected_checksum: $expected_checksum"
if [ "$result_checksum" != "$expected_checksum" ]; then
echo "❌ checksums do not match"
exit 1
else
echo "✅ checksums match"
fi

# Upload the json diff as an artifact
- uses: actions/upload-artifact@v4
with:
name: json-diff
path: ~/diff.json
retention-days: 1
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# git-diff-action 📃

[![test](https://github.com/GrantBirki/git-diff-action/actions/workflows/test.yml/badge.svg)](https://github.com/GrantBirki/git-diff-action/actions/workflows/test.yml) [![CodeQL](https://github.com/GrantBirki/git-diff-action/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/GrantBirki/git-diff-action/actions/workflows/codeql-analysis.yml) [![package-check](https://github.com/GrantBirki/git-diff-action/actions/workflows/package-check.yml/badge.svg)](https://github.com/GrantBirki/git-diff-action/actions/workflows/package-check.yml) [![sample-workflow](https://github.com/GrantBirki/git-diff-action/actions/workflows/sample-workflow.yml/badge.svg)](https://github.com/GrantBirki/git-diff-action/actions/workflows/sample-workflow.yml)
[![test](https://github.com/GrantBirki/git-diff-action/actions/workflows/test.yml/badge.svg)](https://github.com/GrantBirki/git-diff-action/actions/workflows/test.yml) [![CodeQL](https://github.com/GrantBirki/git-diff-action/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/GrantBirki/git-diff-action/actions/workflows/codeql-analysis.yml) [![package-check](https://github.com/GrantBirki/git-diff-action/actions/workflows/package-check.yml/badge.svg)](https://github.com/GrantBirki/git-diff-action/actions/workflows/package-check.yml) [![sample-workflow](https://github.com/GrantBirki/git-diff-action/actions/workflows/sample-workflow.yml/badge.svg)](https://github.com/GrantBirki/git-diff-action/actions/workflows/sample-workflow.yml) [![coverage](./badges/coverage.svg)](./badges/coverage.svg)

A GitHub Action for gathering the `git diff` of a pull request in JSON format or standard `git diff` format

Expand Down
1 change: 1 addition & 0 deletions __tests__/fixtures/diff.json.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fd1804a90206f5d0c05c26e5e92e3ee48d681b037849743792b1f7eed2f5d0ca diff.json
23 changes: 23 additions & 0 deletions __tests__/functions/exec-async.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {execAsync} from '../../src/functions/exec-async'
import {exec} from 'child_process'
import * as util from 'util'

jest.mock('child_process')
jest.mock('util')

describe('execAsync', () => {
it('should call exec and promisify', async () => {
const execMock = jest.fn()
exec.mockImplementation(execMock)
const promisifyMock = jest.fn().mockImplementation(() => execMock)
util.promisify.mockImplementation(promisifyMock)

const cmd = 'echo hello'
const opts = {}

await execAsync(cmd, opts)

expect(promisifyMock).toHaveBeenCalledWith(exec)
expect(execMock).toHaveBeenCalledWith(cmd, opts)
})
})
212 changes: 212 additions & 0 deletions __tests__/functions/git-diff.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import {gitDiff} from '../../src/functions/git-diff'
import * as execAsync from '../../src/functions/exec-async'
import * as core from '@actions/core'
import fs from 'fs'

const infoMock = jest.spyOn(core, 'info')
const debugMock = jest.spyOn(core, 'debug')
const setFailedMock = jest.spyOn(core, 'setFailed')
const warningMock = jest.spyOn(core, 'warning')

beforeEach(() => {
jest.clearAllMocks()

jest.spyOn(core, 'saveState').mockImplementation(() => {})
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'setOutput').mockImplementation(() => {})
jest.spyOn(core, 'setFailed').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})

process.env.INPUT_BASE_BRANCH = 'HEAD^1'
process.env.INPUT_SEARCH_PATH = '.'
process.env.INPUT_MAX_BUFFER_SIZE = '1000000'
process.env.INPUT_FILE_OUTPUT_ONLY = 'false'
process.env.INPUT_GIT_OPTIONS = '--no-color --full-index'
process.env.INPUT_GIT_DIFF_FILE = '__tests__/fixtures/main.diff'
})

test('executes gitDiff', async () => {
const results = await gitDiff()
expect(results.files.length).toBe(5)

const firstFile = results.files[0]
expect(firstFile.path).toBe('custom-endpoints/nightbot.mjs')

const lastFile = results.files[results.files.length - 1]
expect(lastFile.path).toBe('utils/cache-machine.mjs')
expect(lastFile.type).toBe('DeletedFile')
expect(lastFile.chunks[0].changes[0].type).toBe('DeletedLine')
expect(lastFile.chunks[0].changes[0].content).toBe(`// cache url`)
expect(lastFile.chunks[0].changes[0].lineBefore).toBe(1)
expect(lastFile.chunks[0].changes[0]?.lineAfter).toBe(undefined)

expect(results.files[0].type).toBe('ChangedFile')
expect(results.files[0].chunks[0].changes[0].content).toBe(
`import { v4 as uuidv4 } from 'uuid';`
)
expect(results.files[0].chunks[0].changes[0].type).toBe('UnchangedLine')
expect(results.files[0].chunks[0].changes[2].content).toBe(
`import cacheMachine from '../utils/cache-machine.mjs';`
)
expect(results.files[0].chunks[0].changes[2].type).toBe('DeletedLine')

expect(infoMock).toHaveBeenCalledWith('total files changed (raw diff): 5')
expect(infoMock).toHaveBeenCalledWith('total files changed (json diff): 5')
})

test('executes gitDiff with binary files', async () => {
process.env.INPUT_GIT_DIFF_FILE = '__tests__/fixtures/with-binary-files.diff'
const results = await gitDiff()

const firstFile = results.files[0]
expect(firstFile.path).toBe('custom-endpoints/nightbot.mjs')

const lastFile = results.files[results.files.length - 1]
expect(lastFile.path).toBe('utils/cache-machine.mjs')

expect(results.files[0].type).toBe('ChangedFile')
expect(results.files[0].chunks[0].changes[0].content).toBe(
`import { v4 as uuidv4 } from 'uuid';`
)
expect(results.files[0].chunks[0].changes[0].type).toBe('UnchangedLine')
expect(results.files[0].chunks[0].changes[2].content).toBe(
`import cacheMachine from '../utils/cache-machine.mjs';`
)
expect(results.files[0].chunks[0].changes[2].type).toBe('DeletedLine')

expect(results.files.length).toBe(7)
expect(infoMock).toHaveBeenCalledWith(
'reading git diff from file: __tests__/fixtures/with-binary-files.diff'
)
expect(infoMock).toHaveBeenCalledWith('total files changed (raw diff): 7')
expect(infoMock).toHaveBeenCalledWith('total files changed (json diff): 7')
})

// this test case is a bug test
// there is an issue with the 'parseGitDiff' library where if the --binary flag is used, it will break the parsing
// it will still return "some" results, but it will break the parsing and return an incomplete set of results
test('executes gitDiff with binary files and --binary flag and breaks (bug test)', async () => {
process.env.INPUT_GIT_OPTIONS = '--no-color --full-index --binary'
process.env.INPUT_GIT_DIFF_FILE =
'__tests__/fixtures/with-binary-files-and-binary-flag.diff'
const results = await gitDiff()

const firstFile = results.files[0]
expect(firstFile.path).toBe('custom-endpoints/nightbot.mjs')

const lastFile = results.files[results.files.length - 1]
expect(lastFile.path).toBe('kv-cache.js')

expect(results.files.length).toBe(4)
expect(infoMock).toHaveBeenCalledWith(
'reading git diff from file: __tests__/fixtures/with-binary-files-and-binary-flag.diff'
)

// note that the total files changed is 7, but the json diff only has 4 files
expect(infoMock).toHaveBeenCalledWith('total files changed (raw diff): 7')
expect(infoMock).toHaveBeenCalledWith('total files changed (json diff): 4')
})

test('executes gitDiff by using the git binary', async () => {
process.env.INPUT_GIT_DIFF_FILE = 'false'
process.env.INPUT_MAX_BUFFER_SIZE = ''
process.env.INPUT_RAW_DIFF_FILE_OUTPUT = 'diff.txt'
process.env.INPUT_JSON_DIFF_FILE_OUTPUT = 'diff.json'

const diff = fs.readFileSync('__tests__/fixtures/main.diff', 'utf8')
jest.spyOn(execAsync, 'execAsync').mockImplementation(() => {
return {stdout: diff, stderr: null}
})
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {
return true
})

const results = await gitDiff()

expect(results.files.length).toBe(5)

const firstFile = results.files[0]
expect(firstFile.path).toBe('custom-endpoints/nightbot.mjs')

const lastFile = results.files[results.files.length - 1]
expect(lastFile.path).toBe('utils/cache-machine.mjs')
expect(lastFile.type).toBe('DeletedFile')
expect(lastFile.chunks[0].changes[0].type).toBe('DeletedLine')
expect(lastFile.chunks[0].changes[0].content).toBe(`// cache url`)
expect(lastFile.chunks[0].changes[0].lineBefore).toBe(1)
expect(lastFile.chunks[0].changes[0]?.lineAfter).toBe(undefined)

expect(results.files[0].type).toBe('ChangedFile')
expect(results.files[0].chunks[0].changes[0].content).toBe(
`import { v4 as uuidv4 } from 'uuid';`
)
expect(results.files[0].chunks[0].changes[0].type).toBe('UnchangedLine')
expect(results.files[0].chunks[0].changes[2].content).toBe(
`import cacheMachine from '../utils/cache-machine.mjs';`
)
expect(results.files[0].chunks[0].changes[2].type).toBe('DeletedLine')

expect(infoMock).toHaveBeenCalledWith(
'max_buffer_size is not defined, using default of 1000000'
)
expect(infoMock).toHaveBeenCalledWith('total files changed (raw diff): 5')
expect(infoMock).toHaveBeenCalledWith('total files changed (json diff): 5')
expect(debugMock).toHaveBeenCalledWith(
'running git diff command: git --no-pager diff --no-color --full-index HEAD^1 .'
)
})

test('fails due to stderr being returned from the git binary', async () => {
process.env.INPUT_GIT_DIFF_FILE = 'false'

jest.spyOn(execAsync, 'execAsync').mockImplementation(() => {
return {stdout: '', stderr: 'oh no something went wrong'}
})

expect(await gitDiff()).toBe(undefined)

expect(setFailedMock).toHaveBeenCalledWith(
'git diff error: oh no something went wrong'
)
})

test('leaves a warning when --binary is used', async () => {
process.env.INPUT_GIT_DIFF_FILE = 'false'
process.env.INPUT_RAW_DIFF_FILE_OUTPUT = 'diff.txt'
process.env.INPUT_JSON_DIFF_FILE_OUTPUT = 'diff.json'
process.env.INPUT_GIT_OPTIONS = '--no-color --full-index --binary'

const diff = fs.readFileSync('__tests__/fixtures/main.diff', 'utf8')
jest.spyOn(execAsync, 'execAsync').mockImplementation(() => {
return {stdout: diff, stderr: null}
})
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {
return true
})

const results = await gitDiff()
expect(results.files.length).toBe(5)

expect(warningMock).toHaveBeenCalledWith(
'--binary flag is set, this may cause unexpected issues with the diff'
)
})

test('fails when no custom git diff file is found', async () => {
process.env.INPUT_GIT_DIFF_FILE = 'bad-file.txt'

jest.spyOn(execAsync, 'execAsync').mockImplementation(() => {
throw new Error('oh no something really bad happened')
})

try {
await gitDiff()
} catch (error) {
expect(error).toBeInstanceOf(Error)
expect(error.message).toBe('oh no something really bad happened')
expect(setFailedMock).toHaveBeenCalledWith(
'error getting git diff: oh no something really bad happened'
)
}
})
Loading