Skip to content

Commit 9413783

Browse files
authored
Merge pull request #90 from GrantBirki/copilot/fix-89
More Robust Tests 🧪 - Comprehensive test coverage improvements
2 parents 4570309 + 6d47d5e commit 9413783

File tree

5 files changed

+574
-0
lines changed

5 files changed

+574
-0
lines changed

__tests__/functions/json-validator.test.js

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,3 +642,291 @@ 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(
760+
'ajv-formats will not be used with the json-validator'
761+
)
762+
})
763+
764+
test('handles non-array data processing with single document YAML as JSON', async () => {
765+
// This test covers the case where data is not initially an array (line 254)
766+
process.env.INPUT_YAML_AS_JSON = 'true'
767+
process.env.INPUT_ALLOW_MULTIPLE_DOCUMENTS = 'false' // This makes data not an array initially
768+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/yaml_as_json/valid'
769+
process.env.INPUT_JSON_SCHEMA = ''
770+
771+
const result = await jsonValidator(excludeMock)
772+
expect(result.success).toBe(true)
773+
expect(result.passed).toBe(1)
774+
775+
// This should trigger the Array.isArray check and the debug message
776+
expect(debugMock).toHaveBeenCalledWith(
777+
expect.stringMatching('1 object\\(s\\) found in file:')
778+
)
779+
})
780+
781+
test('edge case: empty json_exclude_regex with complex file structure', async () => {
782+
process.env.INPUT_JSON_EXCLUDE_REGEX = ''
783+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/mixture'
784+
process.env.INPUT_JSON_SCHEMA = ''
785+
786+
const result = await jsonValidator(excludeMock)
787+
expect(result.passed + result.failed).toBeGreaterThan(0)
788+
})
789+
790+
test('edge case: complex file patterns with multiple extensions', async () => {
791+
process.env.INPUT_FILES =
792+
'__tests__/fixtures/json/valid/*.json\n__tests__/fixtures/yaml_as_json/valid/*.yaml'
793+
process.env.INPUT_YAML_AS_JSON = 'true'
794+
process.env.INPUT_BASE_DIR = '.'
795+
process.env.INPUT_JSON_SCHEMA = ''
796+
797+
const result = await jsonValidator(excludeMock)
798+
expect(result.passed).toBeGreaterThan(0)
799+
800+
expect(debugMock).toHaveBeenCalledWith(expect.stringMatching('using files:'))
801+
})
802+
803+
test('edge case: malformed custom regexp formats with complex patterns', async () => {
804+
expect.assertions(1)
805+
try {
806+
process.env.INPUT_AJV_CUSTOM_REGEXP_FORMATS =
807+
'valid_format=^[a-z]+$\ninvalid-format'
808+
await jsonValidator(excludeMock)
809+
} catch (e) {
810+
expect(e.message).toContain('is not in expected format "key=regex"')
811+
}
812+
})
813+
814+
test('edge case: schema file skipping logic', async () => {
815+
process.env.INPUT_JSON_SCHEMA = '__tests__/fixtures/schemas/schema1.json'
816+
process.env.INPUT_FILES =
817+
'__tests__/fixtures/schemas/schema1.json\n__tests__/fixtures/json/valid/json1.json'
818+
process.env.INPUT_BASE_DIR = '.'
819+
820+
const result = await jsonValidator(excludeMock)
821+
expect(result.passed).toBe(1) // Only json1.json should be processed, schema1.json should be skipped
822+
823+
expect(debugMock).toHaveBeenCalledWith(
824+
expect.stringMatching('skipping json schema file:')
825+
)
826+
})
827+
828+
test('stress test: large number of custom regex formats', async () => {
829+
const formats = []
830+
for (let i = 0; i < 10; i++) {
831+
formats.push(`format${i}=^test${i}.*$`)
832+
}
833+
834+
process.env.INPUT_AJV_CUSTOM_REGEXP_FORMATS = formats.join('\n')
835+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid'
836+
process.env.INPUT_JSON_SCHEMA = ''
837+
838+
const result = await jsonValidator(excludeMock)
839+
expect(result.success).toBe(true)
840+
})
841+
842+
test('edge case: duplicate file processing prevention', async () => {
843+
// Test that files are not processed multiple times
844+
process.env.INPUT_FILES =
845+
'__tests__/fixtures/json/valid/json1.json\n__tests__/fixtures/json/valid/json1.json'
846+
process.env.INPUT_BASE_DIR = '.'
847+
process.env.INPUT_JSON_SCHEMA = ''
848+
849+
const result = await jsonValidator(excludeMock)
850+
expect(result.passed).toBe(1) // Should only process the file once
851+
852+
expect(debugMock).toHaveBeenCalledWith(
853+
expect.stringMatching('skipping duplicate file:')
854+
)
855+
})
856+
857+
test('edge case: baseDir with trailing slash normalization', async () => {
858+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/json/valid/' // Note trailing slash
859+
process.env.INPUT_JSON_SCHEMA = ''
860+
861+
const result = await jsonValidator(excludeMock)
862+
expect(result.success).toBe(true)
863+
expect(result.passed).toBe(1)
864+
})
865+
866+
test('real world scenario: large schema with draft-2019-09', async () => {
867+
process.env.INPUT_JSON_SCHEMA_VERSION = 'draft-2019-09'
868+
process.env.INPUT_JSON_SCHEMA = '__tests__/fixtures/schemas/challenge.json'
869+
process.env.INPUT_BASE_DIR = '__tests__/fixtures/real_world/challenges'
870+
process.env.INPUT_YAML_AS_JSON = 'true'
871+
872+
const result = await jsonValidator(excludeMock)
873+
expect(result).toBeDefined()
874+
expect(typeof result.success).toBe('boolean')
875+
})
876+
877+
test('edge case: potential non-array data with complex yaml parsing', async () => {
878+
// Create a file with complex YAML that might not result in array
879+
const fs = require('fs')
880+
const tempFile = '/tmp/complex_yaml.yaml'
881+
fs.writeFileSync(tempFile, 'scalar_value')
882+
883+
process.env.INPUT_YAML_AS_JSON = 'true'
884+
process.env.INPUT_ALLOW_MULTIPLE_DOCUMENTS = 'false'
885+
process.env.INPUT_FILES = tempFile
886+
process.env.INPUT_BASE_DIR = '.'
887+
process.env.INPUT_JSON_SCHEMA = ''
888+
889+
const result = await jsonValidator(excludeMock)
890+
expect(result.passed).toBe(1)
891+
892+
// Cleanup
893+
fs.unlinkSync(tempFile)
894+
})
895+
896+
test('edge case: empty document processing', async () => {
897+
// Create an empty YAML file
898+
const fs = require('fs')
899+
const tempFile = '/tmp/empty.yaml'
900+
fs.writeFileSync(tempFile, '')
901+
902+
process.env.INPUT_YAML_AS_JSON = 'true'
903+
process.env.INPUT_ALLOW_MULTIPLE_DOCUMENTS = 'false'
904+
process.env.INPUT_FILES = tempFile
905+
process.env.INPUT_BASE_DIR = '.'
906+
process.env.INPUT_JSON_SCHEMA = ''
907+
908+
const result = await jsonValidator(excludeMock)
909+
expect(result.passed + result.failed).toBeGreaterThan(0)
910+
911+
// Cleanup
912+
fs.unlinkSync(tempFile)
913+
})
914+
915+
test('edge case: malformed JSON in real file', async () => {
916+
// Create a malformed JSON file
917+
const fs = require('fs')
918+
const tempFile = '/tmp/malformed.json'
919+
fs.writeFileSync(tempFile, '{"invalid": json, missing quotes}')
920+
921+
process.env.INPUT_FILES = tempFile
922+
process.env.INPUT_BASE_DIR = '.'
923+
process.env.INPUT_JSON_SCHEMA = ''
924+
process.env.INPUT_YAML_AS_JSON = 'false'
925+
926+
const result = await jsonValidator(excludeMock)
927+
expect(result.failed).toBe(1)
928+
expect(result.success).toBe(false)
929+
930+
// Cleanup
931+
fs.unlinkSync(tempFile)
932+
})

__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)