Skip to content

Commit 27c6361

Browse files
CopilotGrantBirki
andcommitted
Add comprehensive test coverage improvements - branch coverage increased from 93.12% to 97.7%
Co-authored-by: GrantBirki <[email protected]>
1 parent db6e397 commit 27c6361

File tree

5 files changed

+556
-0
lines changed

5 files changed

+556
-0
lines changed

__tests__/functions/json-validator.test.js

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,3 +642,287 @@ test('yamlAsJson: failed validation of a multi-document-file', async () => {
642642
]
643643
})
644644
})
645+
646+
test('successfully skips JSON files that match json_exclude_regex', async () => {
647+
process.env.INPUT_JSON_EXCLUDE_REGEX = '.*valid.*\\.json'
648+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid'
649+
650+
expect(await jsonValidator(excludeMock)).toStrictEqual({
651+
failed: 0,
652+
passed: 0,
653+
skipped: 1,
654+
success: true,
655+
violations: []
656+
})
657+
658+
expect(infoMock).toHaveBeenCalledWith(
659+
expect.stringMatching('skipping due to exclude match:')
660+
)
661+
})
662+
663+
test('handles json_exclude_regex with empty string (no exclusion)', async () => {
664+
process.env.INPUT_JSON_EXCLUDE_REGEX = ''
665+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid'
666+
667+
expect(await jsonValidator(excludeMock)).toStrictEqual({
668+
failed: 0,
669+
passed: 1,
670+
skipped: 0,
671+
success: true,
672+
violations: []
673+
})
674+
})
675+
676+
test('processes non-array data correctly (covers data validation branch)', async () => {
677+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid'
678+
process.env.INPUT_JSON_SCHEMA = ''
679+
680+
const result = await jsonValidator(excludeMock)
681+
expect(result.success).toBe(true)
682+
expect(result.passed).toBe(1)
683+
684+
expect(debugMock).toHaveBeenCalledWith(
685+
expect.stringMatching('1 object\\(s\\) found in file:')
686+
)
687+
})
688+
689+
test('processes a simple example with DRAFT_2020_12 schema version', async () => {
690+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid'
691+
process.env.INPUT_JSON_SCHEMA_VERSION = 'draft-2020-12'
692+
693+
expect(await jsonValidator(excludeMock)).toStrictEqual({
694+
failed: 0,
695+
passed: 1,
696+
skipped: 0,
697+
success: true,
698+
violations: []
699+
})
700+
701+
expect(debugMock).toHaveBeenCalledWith('json_schema_version: draft-2020-12')
702+
})
703+
704+
test('processes yaml as json with DRAFT_2020_12 and multiple documents', async () => {
705+
process.env.INPUT_YAML_AS_JSON = 'true'
706+
process.env.INPUT_ALLOW_MULTIPLE_DOCUMENTS = 'true'
707+
process.env.INPUT_JSON_SCHEMA_VERSION = 'draft-2020-12'
708+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/yaml_as_json/valid_multi'
709+
710+
expect(await jsonValidator(excludeMock)).toStrictEqual({
711+
failed: 0,
712+
passed: 1,
713+
skipped: 0,
714+
success: true,
715+
violations: []
716+
})
717+
})
718+
719+
test('handles invalid ajv strict mode setting', async () => {
720+
process.env.INPUT_AJV_STRICT_MODE = 'false'
721+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid'
722+
723+
expect(await jsonValidator(excludeMock)).toStrictEqual({
724+
failed: 0,
725+
passed: 1,
726+
skipped: 0,
727+
success: true,
728+
violations: []
729+
})
730+
731+
expect(debugMock).toHaveBeenCalledWith('strict: false')
732+
})
733+
734+
test('handles empty ajv_custom_regexp_formats input', async () => {
735+
process.env.INPUT_AJV_CUSTOM_REGEXP_FORMATS = '\n\n'
736+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid'
737+
738+
expect(await jsonValidator(excludeMock)).toStrictEqual({
739+
failed: 0,
740+
passed: 1,
741+
skipped: 0,
742+
success: true,
743+
violations: []
744+
})
745+
})
746+
747+
test('handles use_ajv_formats disabled', async () => {
748+
process.env.INPUT_USE_AJV_FORMATS = 'false'
749+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid'
750+
751+
expect(await jsonValidator(excludeMock)).toStrictEqual({
752+
failed: 0,
753+
passed: 1,
754+
skipped: 0,
755+
success: true,
756+
violations: []
757+
})
758+
759+
expect(debugMock).toHaveBeenCalledWith('ajv-formats will not be used with the json-validator')
760+
})
761+
762+
test('handles non-array data processing with single document YAML as JSON', async () => {
763+
// This test covers the case where data is not initially an array (line 254)
764+
process.env.INPUT_YAML_AS_JSON = 'true'
765+
process.env.INPUT_ALLOW_MULTIPLE_DOCUMENTS = 'false' // This makes data not an array initially
766+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/yaml_as_json/valid'
767+
process.env.INPUT_JSON_SCHEMA = ''
768+
769+
const result = await jsonValidator(excludeMock)
770+
expect(result.success).toBe(true)
771+
expect(result.passed).toBe(1)
772+
773+
// This should trigger the Array.isArray check and the debug message
774+
expect(debugMock).toHaveBeenCalledWith(
775+
expect.stringMatching('1 object\\(s\\) found in file:')
776+
)
777+
})
778+
779+
test('edge case: empty json_exclude_regex with complex file structure', async () => {
780+
process.env.INPUT_JSON_EXCLUDE_REGEX = ''
781+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/mixture'
782+
process.env.INPUT_JSON_SCHEMA = ''
783+
784+
const result = await jsonValidator(excludeMock)
785+
expect(result.passed + result.failed).toBeGreaterThan(0)
786+
})
787+
788+
test('edge case: complex file patterns with multiple extensions', async () => {
789+
process.env.INPUT_FILES = '__tests__/fixtures/json/valid/*.json\n__tests__/fixtures/yaml_as_json/valid/*.yaml'
790+
process.env.INPUT_YAML_AS_JSON = 'true'
791+
process.env.INPUT_BASE_DIR = '.'
792+
process.env.INPUT_JSON_SCHEMA = ''
793+
794+
const result = await jsonValidator(excludeMock)
795+
expect(result.passed).toBeGreaterThan(0)
796+
797+
expect(debugMock).toHaveBeenCalledWith(
798+
expect.stringMatching('using files:')
799+
)
800+
})
801+
802+
test('edge case: malformed custom regexp formats with complex patterns', async () => {
803+
expect.assertions(1)
804+
try {
805+
process.env.INPUT_AJV_CUSTOM_REGEXP_FORMATS = 'valid_format=^[a-z]+$\ninvalid-format'
806+
await jsonValidator(excludeMock)
807+
} catch (e) {
808+
expect(e.message).toContain('is not in expected format "key=regex"')
809+
}
810+
})
811+
812+
test('edge case: schema file skipping logic', async () => {
813+
process.env.INPUT_JSON_SCHEMA = '__tests__/fixtures/schemas/schema1.json'
814+
process.env.INPUT_FILES = '__tests__/fixtures/schemas/schema1.json\n__tests__/fixtures/json/valid/json1.json'
815+
process.env.INPUT_BASE_DIR = '.'
816+
817+
const result = await jsonValidator(excludeMock)
818+
expect(result.passed).toBe(1) // Only json1.json should be processed, schema1.json should be skipped
819+
820+
expect(debugMock).toHaveBeenCalledWith(
821+
expect.stringMatching('skipping json schema file:')
822+
)
823+
})
824+
825+
test('stress test: large number of custom regex formats', async () => {
826+
const formats = []
827+
for (let i = 0; i < 10; i++) {
828+
formats.push(`format${i}=^test${i}.*$`)
829+
}
830+
831+
process.env.INPUT_AJV_CUSTOM_REGEXP_FORMATS = formats.join('\n')
832+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid'
833+
process.env.INPUT_JSON_SCHEMA = ''
834+
835+
const result = await jsonValidator(excludeMock)
836+
expect(result.success).toBe(true)
837+
})
838+
839+
test('edge case: duplicate file processing prevention', async () => {
840+
// Test that files are not processed multiple times
841+
process.env.INPUT_FILES = '__tests__/fixtures/json/valid/json1.json\n__tests__/fixtures/json/valid/json1.json'
842+
process.env.INPUT_BASE_DIR = '.'
843+
process.env.INPUT_JSON_SCHEMA = ''
844+
845+
const result = await jsonValidator(excludeMock)
846+
expect(result.passed).toBe(1) // Should only process the file once
847+
848+
expect(debugMock).toHaveBeenCalledWith(
849+
expect.stringMatching('skipping duplicate file:')
850+
)
851+
})
852+
853+
test('edge case: baseDir with trailing slash normalization', async () => {
854+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid/' // Note trailing slash
855+
process.env.INPUT_JSON_SCHEMA = ''
856+
857+
const result = await jsonValidator(excludeMock)
858+
expect(result.success).toBe(true)
859+
expect(result.passed).toBe(1)
860+
})
861+
862+
test('real world scenario: large schema with draft-2019-09', async () => {
863+
process.env.INPUT_JSON_SCHEMA_VERSION = 'draft-2019-09'
864+
process.env.INPUT_JSON_SCHEMA = '__tests__/fixtures/schemas/challenge.json'
865+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/real_world/challenges'
866+
process.env.INPUT_YAML_AS_JSON = 'true'
867+
868+
const result = await jsonValidator(excludeMock)
869+
expect(result).toBeDefined()
870+
expect(typeof result.success).toBe('boolean')
871+
})
872+
873+
test('edge case: potential non-array data with complex yaml parsing', async () => {
874+
// Create a file with complex YAML that might not result in array
875+
const fs = require('fs')
876+
const tempFile = '/tmp/complex_yaml.yaml'
877+
fs.writeFileSync(tempFile, 'scalar_value')
878+
879+
process.env.INPUT_YAML_AS_JSON = 'true'
880+
process.env.INPUT_ALLOW_MULTIPLE_DOCUMENTS = 'false'
881+
process.env.INPUT_FILES = tempFile
882+
process.env.INPUT_BASE_DIR = '.'
883+
process.env.INPUT_JSON_SCHEMA = ''
884+
885+
const result = await jsonValidator(excludeMock)
886+
expect(result.passed).toBe(1)
887+
888+
// Cleanup
889+
fs.unlinkSync(tempFile)
890+
})
891+
892+
test('edge case: empty document processing', async () => {
893+
// Create an empty YAML file
894+
const fs = require('fs')
895+
const tempFile = '/tmp/empty.yaml'
896+
fs.writeFileSync(tempFile, '')
897+
898+
process.env.INPUT_YAML_AS_JSON = 'true'
899+
process.env.INPUT_ALLOW_MULTIPLE_DOCUMENTS = 'false'
900+
process.env.INPUT_FILES = tempFile
901+
process.env.INPUT_BASE_DIR = '.'
902+
process.env.INPUT_JSON_SCHEMA = ''
903+
904+
const result = await jsonValidator(excludeMock)
905+
expect(result.passed + result.failed).toBeGreaterThan(0)
906+
907+
// Cleanup
908+
fs.unlinkSync(tempFile)
909+
})
910+
911+
test('edge case: malformed JSON in real file', async () => {
912+
// Create a malformed JSON file
913+
const fs = require('fs')
914+
const tempFile = '/tmp/malformed.json'
915+
fs.writeFileSync(tempFile, '{"invalid": json, missing quotes}')
916+
917+
process.env.INPUT_FILES = tempFile
918+
process.env.INPUT_BASE_DIR = '.'
919+
process.env.INPUT_JSON_SCHEMA = ''
920+
process.env.INPUT_YAML_AS_JSON = 'false'
921+
922+
const result = await jsonValidator(excludeMock)
923+
expect(result.failed).toBe(1)
924+
expect(result.success).toBe(false)
925+
926+
// Cleanup
927+
fs.unlinkSync(tempFile)
928+
})

__tests__/functions/process-results.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,44 @@ test('fails the action due to yaml AND json errors - comment mode enabled', asyn
266266
'❌ JSON or YAML files failed validation'
267267
)
268268
})
269+
test('tests constructBody function with JSON failures only (covers lines 50-67)', async () => {
270+
process.env.INPUT_COMMENT = 'true'
271+
expect(
272+
await processResults(
273+
{
274+
success: false,
275+
failed: 1,
276+
passed: 0,
277+
skipped: 0,
278+
violations: jsonViolations
279+
},
280+
{success: true, failed: 0, passed: 3, skipped: 0, violations: []}
281+
)
282+
).toBe(false)
283+
284+
// The constructBody function should have been called and created a comment
285+
expect(infoMock).toHaveBeenCalledWith(
286+
expect.stringMatching('📝 adding comment to PR')
287+
)
288+
})
289+
290+
test('tests constructBody function with YAML failures only (covers lines 69-86)', async () => {
291+
process.env.INPUT_COMMENT = 'true'
292+
expect(
293+
await processResults(
294+
{success: true, failed: 0, passed: 5, skipped: 0, violations: []},
295+
{
296+
success: false,
297+
failed: 1,
298+
passed: 0,
299+
skipped: 0,
300+
violations: yamlViolations
301+
}
302+
)
303+
).toBe(false)
304+
305+
// The constructBody function should have been called and created a comment
306+
expect(infoMock).toHaveBeenCalledWith(
307+
expect.stringMatching('📝 adding comment to PR')
308+
)
309+
})

0 commit comments

Comments
 (0)