UNPKG

@aaronshaf/ger

Version:

Gerrit CLI and SDK - A modern CLI tool and TypeScript SDK for Gerrit Code Review, built with Effect-TS

133 lines (108 loc) 4.77 kB
import { test, expect, describe } from 'bun:test' import { CHANGE_ID_PATTERN } from '@/services/commit-hook' // Tests for commit-hook service patterns // Note: Tests that require actual git operations are skipped in the full test suite // due to mock pollution from other test files. Run these tests in isolation for full coverage: // bun test tests/unit/services/commit-hook.test.ts describe('Commit Hook Service', () => { describe('CHANGE_ID_PATTERN', () => { test('should match valid Change-Id', () => { const validIds = [ 'Change-Id: I1234567890123456789012345678901234567890', 'Change-Id: Iabcdefabcdefabcdefabcdefabcdefabcdefabcd', 'Change-Id: I0000000000000000000000000000000000000000', 'Change-Id: Iffffffffffffffffffffffffffffffffffffffff', ] for (const id of validIds) { expect(CHANGE_ID_PATTERN.test(id)).toBe(true) } }) test('should not match invalid Change-Id', () => { const invalidIds = [ 'Change-Id: 1234567890123456789012345678901234567890', // Missing I prefix 'Change-Id: I123456789012345678901234567890123456789', // Too short (39 chars) 'Change-Id: I12345678901234567890123456789012345678901', // Too long (41 chars) 'Change-Id: IGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG', // Invalid hex chars 'Change-Id: i1234567890123456789012345678901234567890', // Lowercase I 'change-id: I1234567890123456789012345678901234567890', // Lowercase prefix ] for (const id of invalidIds) { expect(CHANGE_ID_PATTERN.test(id)).toBe(false) } }) test('should match Change-Id in multiline commit message', () => { const commitMessage = `Fix authentication bug This commit fixes the login issue where users were being logged out unexpectedly. Change-Id: I1234567890123456789012345678901234567890 Signed-off-by: Test User <test@example.com>` expect(CHANGE_ID_PATTERN.test(commitMessage)).toBe(true) }) test('should not match Change-Id in wrong position', () => { // Change-Id should be at start of line const wrongPosition = ' Change-Id: I1234567890123456789012345678901234567890' expect(CHANGE_ID_PATTERN.test(wrongPosition)).toBe(false) }) }) describe('hook path patterns', () => { test('should construct correct hooks directory path', () => { const gitDir = '.git' const hooksDir = `${gitDir}/hooks` expect(hooksDir).toBe('.git/hooks') }) test('should construct correct commit-msg hook path', () => { const gitDir = '.git' const hookPath = `${gitDir}/hooks/commit-msg` expect(hookPath).toBe('.git/hooks/commit-msg') }) test('should handle absolute git dir path', () => { const gitDir = '/home/user/project/.git' const hookPath = `${gitDir}/hooks/commit-msg` expect(hookPath).toBe('/home/user/project/.git/hooks/commit-msg') }) }) describe('hook URL construction', () => { test('should construct correct hook URL', () => { const host = 'https://gerrit.example.com' const hookUrl = `${host}/tools/hooks/commit-msg` expect(hookUrl).toBe('https://gerrit.example.com/tools/hooks/commit-msg') }) test('should handle host with trailing slash', () => { const host = 'https://gerrit.example.com/' const normalizedHost = host.replace(/\/$/, '') const hookUrl = `${normalizedHost}/tools/hooks/commit-msg` expect(hookUrl).toBe('https://gerrit.example.com/tools/hooks/commit-msg') }) }) describe('hook content validation', () => { test('should validate shell script header', () => { const validHook = '#!/bin/sh\necho "Adding Change-Id"' expect(validHook.startsWith('#!')).toBe(true) }) test('should reject non-script content', () => { const invalidHook = 'This is not a script' expect(invalidHook.startsWith('#!')).toBe(false) }) test('should validate bash script header', () => { const bashHook = '#!/bin/bash\necho "Adding Change-Id"' expect(bashHook.startsWith('#!')).toBe(true) }) }) describe('executable bit checking', () => { test('should identify executable mode', () => { const executableMode = 0o755 const ownerExecuteBit = 0o100 expect((executableMode & ownerExecuteBit) !== 0).toBe(true) }) test('should identify non-executable mode', () => { const nonExecutableMode = 0o644 const ownerExecuteBit = 0o100 expect((nonExecutableMode & ownerExecuteBit) !== 0).toBe(false) }) test('should handle read-only mode', () => { const readOnlyMode = 0o444 const ownerExecuteBit = 0o100 expect((readOnlyMode & ownerExecuteBit) !== 0).toBe(false) }) }) })