Repository: harness/canary
Pull request #1176: chore: add more tests in transform utils folder
Author: login: cjlee01
Created: 2025-03-01T07:39:43Z | Merged: 2025-03-04T00:14:17Z

Add unit tests in transform utils folder


Diff:
diff --git a/apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts b/apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
new file mode 100644
index 0000000000..323b4fbb24
--- /dev/null
+++ b/apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
@@ -0,0 +1,294 @@
+import { TypesCodeOwnerEvaluationEntry, TypesOwnerEvaluation, TypesPrincipalInfo } from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+export enum EnumCheckStatus {
+  Error = 'error',
+  Failure = 'failure',
+  Pending = 'pending',
+  Running = 'running',
+  Success = 'success'
+}
+export enum EnumPullReqReviewDecision {
+  Approved = 'approved',
+  ChangeReq = 'changereq',
+  Pending = 'pending',
+  Reviewed = 'reviewed',
+  Waiting = 'waiting'
+}
+
+// Mock data
+const mockChecks = [
+  { check: { status: EnumCheckStatus.Success }, required: true, bypassable: false },
+  { check: { status: EnumCheckStatus.Failure }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = EnumPullReqReviewDecision.Approved
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    pattern: '**',
+    owner_evaluations: [
+      {
+        owner: {
+          id: 3,
+          uid: 'admin',
+          display_name: 'Administrator',
+          email: 'admin@gitness.io',
+          type: 'user',
+          created: 1699863416002,
+          updated: 1699863416002
+        },
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        review_decision: '' as any, // Set to empty
+        review_sha: ''
+      }
+    ],
+    user_group_owner_evaluations: []
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: EnumPullReqReviewDecision.Approved },
+      { review_decision: EnumPullReqReviewDecision.ChangeReq }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: EnumPullReqReviewDecision.Approved }]
+  },
+  {
+    owner_evaluations: [{ review_decision: EnumPullReqReviewDecision.Reviewed }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+
+  it('should not mark review as outdated if SHAs do not differ', () => {
+    const sameSHA = 'sameSHA'
+    const result = processReviewDecision(mockReviewDecision, sameSHA, sameSHA)
+    expect(result).not.toBe(PullReqReviewDecision.outdated) // Assuming it returns a different decision
+  })
+  it('should return the original decision if not approved or SHAs are the same', () => {
+    const result = processReviewDecision(PullReqReviewDecision.pending, 'sameSHA', 'sameSHA')
+    expect(result).toBe(PullReqReviewDecision.pending)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'changereq')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'changereq' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([
+      {
+        pattern: '**',
+        owner_evaluations: [
+          {
+            owner: {
+              id: 3,
+              uid: 'admin',
+              display_name: 'Administrator',
+              email: 'admin@gitness.io',
+              type: 'user',
+              created: 1699863416002,
+              updated: 1699863416002
+            },
+            review_decision: '',
+            review_sha: ''
+          }
+        ],
+        user_group_owner_evaluations: []
+      }
+    ])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should remove trailing tab if path contains spaces', () => {
+    const pathWithSpacesAndTab = 'path/with a space/file.txt\t'
+    const normalizedPath = normalizeGitFilePath(pathWithSpacesAndTab)
+    expect(normalizedPath).toBe('path/with a space/file.txt')
+  })
+})
+
+describe('generateAlphaNumericHash', () => {
+  it('should generate an alphanumeric hash of specified length', () => {
+    const result = generateAlphaNumericHash(5)
+    expect(result).toHaveLength(5)
+  })
+})
+
+// Mock data for extractInfoForCodeOwnerContent
+const mockPropsForCodeOwnerContent = {
+  approvedEvaluations: [{ reviewer: { display_name: 'John Doe' } as TypesPrincipalInfo, approved: true }],
+  reqNoChangeReq: false,
+  reqCodeOwnerApproval: true,
+  minApproval: 1,
+  reqCodeOwnerLatestApproval: false,
+  minReqLatestApproval: 0,
+  codeOwnerChangeReqEntries: [],
+  codeOwnerPendingEntries: [],
+  latestCodeOwnerApprovalArr: [],
+  latestApprovalArr: [],
+  codeOwnerApprovalEntries: [
+    {
+      owner_evaluations: [
+        { reviewer: { display_name: 'Jane Smith' } as TypesPrincipalInfo, approved: true } as TypesOwnerEvaluation
+      ]
+    }
+  ],
+  changeReqReviewer: 'John Doe',
+  changeReqEvaluations: []
+}
+
+describe('extractInfoForCodeOwnerContent', () => {
+  it('should extract information for code owner content', () => {
+    const result = extractInfoForCodeOwnerContent(mockPropsForCodeOwnerContent)
+    expect(result).toEqual({
+      title: 'Changes approved',
+      statusMessage: 'Changes were approved by code owners',
+      statusColor: 'text-success',
+      statusIcon: 'success',
+      isNotRequired: false
+    })
+  })
+})
+
+describe('changedFileId', () => {
+  it('should return the changed file ID', () => {
+    const result = changedFileId(['file.txt'])
+    expect(result).toBe('file.txt')
+  })
+
+  it('should handle multiple changed files', () => {
+    const result = changedFileId(['file1.txt', 'file2.txt'])
+    expect(result).toBe('file1.txt::::file2.txt')
+  })
+
+  it('should handle no changed files', () => {
+    const result = changedFileId([])
+    expect(result).toBe('')
+  })
+})
+
+describe('extractInfoFromRuleViolationArr', () => {
+  it('should extract information from rule violation array', () => {
+    const result = extractInfoFromRuleViolationArr(mockRuleViolations)
+    expect(result).toEqual({
+      checkIfBypassAllowed: false,
+      uniqueViolations: new Set(['Violation 1', 'Violation 2', 'Violation 3']),
+      violationArr: [
+        {
+          violation: 'Violation 1'
+        },
+        {
+          violation: 'Violation 2'
+        },
+        {
+          violation: 'Violation 3'
+        }
+      ]
+    })
+  })
+})
diff --git a/apps/gitness/src/pages-v2/pull-request/pull-request-compare.tsx b/apps/gitness/src/pages-v2/pull-request/pull-request-compare.tsx
index 9007e4b23c..1c7e4f32ac 100644
--- a/apps/gitness/src/pages-v2/pull-request/pull-request-compare.tsx
+++ b/apps/gitness/src/pages-v2/pull-request/pull-request-compare.tsx
@@ -43,10 +43,10 @@ import { parseSpecificDiff } from '../../pages/pull-request/diff-utils'
 import { changesInfoAtom, DiffFileEntry } from '../../pages/pull-request/types/types'
 import { changedFileId, DIFF2HTML_CONFIG, normalizeGitFilePath } from '../../pages/pull-request/utils'
 import { PathParams } from '../../RouteDefinitions'
+import { getErrorMessage } from '../../utils/error-utils'
 import { normalizeGitRef } from '../../utils/git-utils'
 import { useGetRepoLabelAndValuesData } from '../repo/labels/hooks/use-get-repo-label-and-values-data'
 import { useRepoCommitsStore } from '../repo/stores/repo-commits-store'
-import { getErrorMessage } from './pull-request-utils'
 
 /**
  * TODO: This code was migrated from V2 and needs to be refactored.
@@ -538,7 +538,7 @@ export const CreatePullRequest = () => {
               onSelectBranchorTag={(branchTagName, type) => selectBranchorTag(branchTagName, type, false)}
               selectedBranch={selectedTargetBranch}
             />
-            <Icon name="arrow-long" size={12} className="text-icons-1 rotate-180" />
+            <Icon name="arrow-long" size={12} className="rotate-180 text-icons-1" />
             <BranchSelectorContainer
               onSelectBranchorTag={(branchTagName, type) => selectBranchorTag(branchTagName, type, true)}
               selectedBranch={selectedSourceBranch}
diff --git a/apps/gitness/src/pages-v2/pull-request/pull-request-conversation.tsx b/apps/gitness/src/pages-v2/pull-request/pull-request-conversation.tsx
index 5d0ddb220f..175af3de21 100644
--- a/apps/gitness/src/pages-v2/pull-request/pull-request-conversation.tsx
+++ b/apps/gitness/src/pages-v2/pull-request/pull-request-conversation.tsx
@@ -39,20 +39,21 @@ import { useGetRepoRef } from '../../framework/hooks/useGetRepoPath'
 import { useMFEContext } from '../../framework/hooks/useMFEContext'
 import { useQueryState } from '../../framework/hooks/useQueryState'
 import { useTranslationStore } from '../../i18n/stores/i18n-store'
-import {
-  capitalizeFirstLetter,
-  checkIfOutdatedSha,
-  extractInfoForCodeOwnerContent,
-  findChangeReqDecisions,
-  findWaitingDecisions
-} from '../../pages/pull-request/utils'
 import { PathParams } from '../../RouteDefinitions'
 import { CodeOwnerReqDecision } from '../../types'
 import { filenameToLanguage } from '../../utils/git-utils'
 import { usePrConversationLabels } from './hooks/use-pr-conversation-labels'
 import { usePrFilters } from './hooks/use-pr-filters'
 import { usePRCommonInteractions } from './hooks/usePRCommonInteractions'
-import { extractInfoFromRuleViolationArr, processReviewDecision } from './pull-request-utils'
+import {
+  capitalizeFirstLetter,
+  checkIfOutdatedSha,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  processReviewDecision
+} from './pull-request-utils'
 import { usePullRequestProviderStore } from './stores/pull-request-provider-store'
 
 const onCopyClick = (commentId?: number, isNotCodeComment = false) => {
diff --git a/apps/gitness/src/pages-v2/repo/transform-utils/__tests__/branch-transform.test.ts b/apps/gitness/src/pages-v2/repo/transform-utils/__tests__/branch-transform.test.ts
new file mode 100644
index 0000000000..b2632c9e68
--- /dev/null
+++ b/apps/gitness/src/pages-v2/repo/transform-utils/__tests__/branch-transform.test.ts
@@ -0,0 +1,73 @@
+import { ListBranchesOkResponse, TypesBranchExtended, TypesCommitDivergence } from '@harnessio/code-service-client'
+
+import { apiBranches2BranchNames, apiBranches2DefaultBranchName, transformBranchList } from '../branch-transform'
+
+// Mock data
+const mockBranches: TypesBranchExtended[] = [
+  {
+    name: 'main',
+    commit: { sha: 'abc123', committer: { when: '2025-01-02T00:00:00Z', identity: { name: 'John Doe' } } },
+    is_default: true
+  },
+  {
+    name: 'feature',
+    commit: { sha: 'def456', committer: { when: '2025-01-02T00:00:00Z', identity: { name: 'Jane Smith' } } },
+    is_default: false
+  }
+]
+const mockDivergence: TypesCommitDivergence[] = [
+  { ahead: 5, behind: 3 },
+  { ahead: 2, behind: 1 }
+]
+
+const mockApiBranches: ListBranchesOkResponse = [
+  { name: 'main', is_default: true },
+  { name: 'feature', is_default: false }
+]
+
+describe('transformBranchList', () => {
+  beforeAll(() => {
+    // Set the system time to a specific date
+    vi.setSystemTime(new Date('2025-01-03T00:00:00Z'))
+  })
+
+  it('should transform branch data correctly', () => {
+    const result = transformBranchList(mockBranches, 'main', mockDivergence)
+    expect(result).toEqual([
+      {
+        id: 0,
+        name: 'main',
+        sha: 'abc123',
+        timestamp: 'yesterday', // Adjust this according to your expectations
+        default: true,
+        user: { name: 'John Doe', avatarUrl: '' },
+        behindAhead: { behind: 3, ahead: 5, default: true },
+        pullRequests: []
+      },
+      {
+        id: 1,
+        name: 'feature',
+        sha: 'def456',
+        timestamp: 'yesterday', // Adjust this according to your expectations
+        default: false,
+        user: { name: 'Jane Smith', avatarUrl: '' },
+        behindAhead: { behind: 1, ahead: 2, default: false },
+        pullRequests: []
+      }
+    ])
+  })
+})
+
+describe('apiBranches2BranchNames', () => {
+  it('should extract branch names from API response', () => {
+    const result = apiBranches2BranchNames(mockApiBranches)
+    expect(result).toEqual(['main', 'feature'])
+  })
+})
+
+describe('apiBranches2DefaultBranchName', () => {
+  it('should extract default branch name from API response', () => {
+    const result = apiBranches2DefaultBranchName(mockApiBranches)
+    expect(result).toBe('main')
+  })
+})
diff --git a/apps/gitness/src/pages-v2/repo/transform-utils/__tests__/pipeline-list-transform.test.ts b/apps/gitness/src/pages-v2/repo/transform-utils/__tests__/pipeline-list-transform.test.ts
new file mode 100644
index 0000000000..51b4a04e4f
--- /dev/null
+++ b/apps/gitness/src/pages-v2/repo/transform-utils/__tests__/pipeline-list-transform.test.ts
@@ -0,0 +1,32 @@
+import { EnumCiStatus, ListPipelinesOkResponse } from '@harnessio/code-service-client'
+
+import { apiPipelines2Pipelines } from '../pipeline-list-transform'
+
+// Mock data
+const mockPipelines: ListPipelinesOkResponse = [
+  {
+    identifier: 'pipeline1',
+    execution: { message: 'Execution message', status: 'SUCCESS' as EnumCiStatus, after: 'sha123' },
+    last_executions: [{ number: 1 }],
+    created: 1741027200000 // '2025-03-01T00:00:00Z' in milliseconds since Unix epoch
+  }
+]
+
+// Tests
+
+describe('apiPipelines2Pipelines', () => {
+  it('should transform pipeline data correctly', () => {
+    const result = apiPipelines2Pipelines(mockPipelines)
+    expect(result).toEqual([
+      {
+        id: 'pipeline1',
+        description: 'Execution message',
+        meter: [{ id: '1', state: 0 }],
+        name: 'pipeline1',
+        sha: 'sha123',
+        timestamp: 1741027200000,
+        status: 'unknown'
+      }
+    ])
+  })
+})
diff --git a/apps/gitness/src/pages-v2/repo/transform-utils/__tests__/repo-list-transform.test.ts b/apps/gitness/src/pages-v2/repo/transform-utils/__tests__/repo-list-transform.test.ts
new file mode 100644
index 0000000000..566508a608
--- /dev/null
+++ b/apps/gitness/src/pages-v2/repo/transform-utils/__tests__/repo-list-transform.test.ts
@@ -0,0 +1,40 @@
+import { RepoRepositoryOutput } from '@harnessio/code-service-client'
+
+import { transformRepoList } from '../repo-list-transform'
+
+// Mock data
+const mockRepos: RepoRepositoryOutput[] = [
+  {
+    id: 1,
+    identifier: 'repo1',
+    description: 'A sample repository',
+    is_public: false,
+    num_forks: 5,
+    num_pulls: 10,
+    updated: 1617181723,
+    created: 1617181723,
+    importing: false
+  }
+]
+
+// Tests
+
+describe('transformRepoList', () => {
+  it('should transform repository data correctly', () => {
+    const result = transformRepoList(mockRepos)
+    expect(result).toEqual([
+      {
+        id: 1,
+        name: 'repo1',
+        description: 'A sample repository',
+        private: true,
+        stars: 0,
+        forks: 5,
+        pulls: 10,
+        timestamp: 'Jan 19, 1970', // Assuming timeAgoFromEpochTime returns 'just now'
+        createdAt: 1617181723,
+        importing: false
+      }
+    ])
+  })
+})
diff --git a/packages/ui/src/views/repo/pull-request/details/components/conversation/sections/pull-request-changes-section.tsx b/packages/ui/src/views/repo/pull-request/details/components/conversation/sections/pull-request-changes-section.tsx
index cd41477f66..5a95145de5 100644
--- a/packages/ui/src/views/repo/pull-request/details/components/conversation/sections/pull-request-changes-section.tsx
+++ b/packages/ui/src/views/repo/pull-request/details/components/conversation/sections/pull-request-changes-section.tsx
@@ -204,10 +204,11 @@ const PullRequestChangesSection = ({
   }
 
   const viewBtn =
-    (minApproval && minApproval > 0 && !isEmpty(approvedEvaluations)) ||
-    (minReqLatestApproval && minReqLatestApproval > 0 && !isEmpty(latestApprovalArr)) ||
+    (minApproval && minApproval > 0) ||
+    (minReqLatestApproval && minReqLatestApproval > 0) ||
     !isEmpty(changeReqEvaluations) ||
     (!isEmpty(codeOwners) && !isEmpty(codeOwners?.evaluation_entries))
+
   return (
     <Accordion.Item value="item-1">
       <Accordion.Trigger


Reviews:
user: login: vardanbansal-harness
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-01T07:59:14Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T11:15:02Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T11:16:16Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T11:17:14Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T11:19:51Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T11:20:16Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T11:58:42Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:00:08Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:04:13Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:04:47Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:05:55Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:07:47Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:08:56Z
user: login: knagurski
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T12:11:05Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T21:34:27Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T21:35:04Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T21:35:24Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T21:47:32Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T21:51:33Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T22:15:43Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T22:22:07Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T22:23:03Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T22:23:10Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T22:28:23Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T22:32:25Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T22:37:18Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T22:37:32Z
user: login: cjlee01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T23:32:48Z

Review comments:
diff_hunk: @@ -0,0 +1,82 @@
+import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
+
+import { ListBranchesOkResponse, TypesBranchExtended, TypesCommitDivergence } from '@harnessio/code-service-client'
+
+import { apiBranches2BranchNames, apiBranches2DefaultBranchName, transformBranchList } from '../branch-transform'
+
+// Mock data
+const mockBranches: TypesBranchExtended[] = [
+  {
+    name: 'main',
+    commit: { sha: 'abc123', committer: { when: '2025-01-02T00:00:00Z', identity: { name: 'John Doe' } } },
+    is_default: true
+  },
+  {
+    name: 'feature',
+    commit: { sha: 'def456', committer: { when: '2025-01-02T00:00:00Z', identity: { name: 'Jane Smith' } } },
+    is_default: false
+  }
+]
+const mockDivergence: TypesCommitDivergence[] = [
+  { ahead: 5, behind: 3 },
+  { ahead: 2, behind: 1 }
+]
+
+const mockApiBranches: ListBranchesOkResponse = [
+  { name: 'main', is_default: true },
+  { name: 'feature', is_default: false }
+]
+
+describe('transformBranchList', () => {
+  beforeAll(() => {
+    // Use fake timers to control the current time
+    vi.useFakeTimers()
+    // Set the system time to a specific date
+    vi.setSystemTime(new Date('2025-01-03T00:00:00Z'))
+  })
+
+  afterAll(() => {
+    // Restore real timers after tests
+    vi.useRealTimers()
+  })
path: apps/gitness/src/pages-v2/repo/transform-utils/__tests__/branch-transform.test.ts
user: login: knagurski
body: Faker timers controls things like `setTimeout` and `setInterval`, not system time. You can kill them off with these tests.
```suggestion
  beforeAll(() => {
    // Set the system time to a specific date
    vi.setSystemTime(new Date('2025-01-03T00:00:00Z'))
  })
```
created_at: 2025-03-03T11:15:02Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,82 @@
+import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
path: apps/gitness/src/pages-v2/repo/transform-utils/__tests__/branch-transform.test.ts
user: login: knagurski
body: You don't need these at all since they're already set up as globals
```suggestion
```
created_at: 2025-03-03T11:16:16Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: No need to import since they're already set as globals.
```suggestion
```
created_at: 2025-03-03T11:17:14Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: So `EnumCheckStatus` isn't an enum 🤦🏻‍♂️ 
created_at: 2025-03-03T11:19:50Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: And `EnumPullReqReviewDecision` isn't an enum either 🤦🏻‍♂️.
created_at: 2025-03-03T11:20:16Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should normalize file paths with spaces', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: This test seems a bit weird. It seems to show that `normalizeGitFilePath` will have no effect of a path with spaces. So it doesn't really prove what the function is supposed to do, only that it doesn't do anything with this input.
created_at: 2025-03-03T11:58:42Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: This test doesn't really prove that it can find waiting decisions, rather it just returns an empty array. Can we modify the test data to include waiting decisions that can be picked up?
created_at: 2025-03-03T12:00:08Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: Had a quick look at the function and I'm not really sure what it's meant to do. It will make the first letter capital, but it will also lower-case the rest of the string. I can't see it used anywhere, but it seems weird.

e.g.
```typescript
capitalizeFirstLetter('Hello there. How was your day Jimmy?')
// -> Hello there. how was your day jimmy?
```
created_at: 2025-03-03T12:04:13Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: Would be good to prove when it would return `false` as well.
created_at: 2025-03-03T12:04:46Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: Would be good to prove what happens when SHAs don't differ too
created_at: 2025-03-03T12:05:54Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should normalize file paths with spaces', () => {
+    const result = normalizeGitFilePath('path/with space/file.txt')
+    expect(result).toBe('path/with space/file.txt')
+  })
+})
+
+describe('generateAlphaNumericHash', () => {
+  it('should generate an alphanumeric hash of specified length', () => {
+    const result = generateAlphaNumericHash(5)
+    expect(result).toHaveLength(5)
+  })
+})
+
+describe('getErrorMessage', () => {
+  it('should return an error message from an error object', () => {
+    const error = new Error('Test error')
+    const result = getErrorMessage(error)
+    expect(result).toBe('Test error')
+  })
+})
+
+// Mock data for extractInfoForCodeOwnerContent
+const mockPropsForCodeOwnerContent = {
+  approvedEvaluations: [{ reviewer: { display_name: 'John Doe' } as TypesPrincipalInfo, approved: true }],
+  reqNoChangeReq: false,
+  reqCodeOwnerApproval: true,
+  minApproval: 1,
+  reqCodeOwnerLatestApproval: false,
+  minReqLatestApproval: 0,
+  codeOwnerChangeReqEntries: [],
+  codeOwnerPendingEntries: [],
+  latestCodeOwnerApprovalArr: [],
+  latestApprovalArr: [],
+  codeOwnerApprovalEntries: [
+    {
+      owner_evaluations: [
+        { reviewer: { display_name: 'Jane Smith' } as TypesPrincipalInfo, approved: true } as TypesOwnerEvaluation
+      ]
+    }
+  ],
+  changeReqReviewer: 'John Doe',
+  changeReqEvaluations: []
+}
+
+describe('extractInfoForCodeOwnerContent', () => {
+  it('should extract information for code owner content', () => {
+    const result = extractInfoForCodeOwnerContent(mockPropsForCodeOwnerContent)
+    expect(result).toEqual({
+      title: 'Changes approved',
+      statusMessage: 'Changes were approved by code owners',
+      statusColor: 'text-success',
+      statusIcon: 'success',
+      isNotRequired: false
+    })
+  })
+})
+
+describe('changedFileId', () => {
+  it('should return the changed file ID', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: Might be good to show what happens when there are multiple changed files? Or none?
created_at: 2025-03-03T12:07:47Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should normalize file paths with spaces', () => {
+    const result = normalizeGitFilePath('path/with space/file.txt')
+    expect(result).toBe('path/with space/file.txt')
+  })
+})
+
+describe('generateAlphaNumericHash', () => {
+  it('should generate an alphanumeric hash of specified length', () => {
+    const result = generateAlphaNumericHash(5)
+    expect(result).toHaveLength(5)
+  })
+})
+
+describe('getErrorMessage', () => {
+  it('should return an error message from an error object', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: Looks like the function can extract from a few places... some tests to show that would be great
created_at: 2025-03-03T12:08:56Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should normalize file paths with spaces', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: knagurski
body: This seems to prove that nothing will happen if the path as a space. Can we get a test that prove what this function will do?
created_at: 2025-03-03T12:11:05Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,82 @@
+import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
+
+import { ListBranchesOkResponse, TypesBranchExtended, TypesCommitDivergence } from '@harnessio/code-service-client'
+
+import { apiBranches2BranchNames, apiBranches2DefaultBranchName, transformBranchList } from '../branch-transform'
+
+// Mock data
+const mockBranches: TypesBranchExtended[] = [
+  {
+    name: 'main',
+    commit: { sha: 'abc123', committer: { when: '2025-01-02T00:00:00Z', identity: { name: 'John Doe' } } },
+    is_default: true
+  },
+  {
+    name: 'feature',
+    commit: { sha: 'def456', committer: { when: '2025-01-02T00:00:00Z', identity: { name: 'Jane Smith' } } },
+    is_default: false
+  }
+]
+const mockDivergence: TypesCommitDivergence[] = [
+  { ahead: 5, behind: 3 },
+  { ahead: 2, behind: 1 }
+]
+
+const mockApiBranches: ListBranchesOkResponse = [
+  { name: 'main', is_default: true },
+  { name: 'feature', is_default: false }
+]
+
+describe('transformBranchList', () => {
+  beforeAll(() => {
+    // Use fake timers to control the current time
+    vi.useFakeTimers()
+    // Set the system time to a specific date
+    vi.setSystemTime(new Date('2025-01-03T00:00:00Z'))
+  })
+
+  afterAll(() => {
+    // Restore real timers after tests
+    vi.useRealTimers()
+  })
path: apps/gitness/src/pages-v2/repo/transform-utils/__tests__/branch-transform.test.ts
user: login: cjlee01
body: done
created_at: 2025-03-03T21:34:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
path: apps/gitness/src/pages-v2/repo/transform-utils/__tests__/branch-transform.test.ts
user: login: cjlee01
body: done
created_at: 2025-03-03T21:35:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: done
created_at: 2025-03-03T21:35:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: done
created_at: 2025-03-03T21:47:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: done
created_at: 2025-03-03T21:51:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should normalize file paths with spaces', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: The function checks if the file path ends with a tab character (\t) and contains a space. If both conditions are met, it removes the trailing tab character. added a test to check that 

created_at: 2025-03-03T22:15:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: was used on the pull request conversation page to get user names just to capitalize first letter
created_at: 2025-03-03T22:22:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should normalize file paths with spaces', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: initially understood it wrong
created_at: 2025-03-03T22:23:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should normalize file paths with spaces', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: The function checks if the file path ends with a tab character (\t) and contains a space. If both conditions are met, it removes the trailing tab character. added a test to check that
created_at: 2025-03-03T22:23:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should normalize file paths with spaces', () => {
+    const result = normalizeGitFilePath('path/with space/file.txt')
+    expect(result).toBe('path/with space/file.txt')
+  })
+})
+
+describe('generateAlphaNumericHash', () => {
+  it('should generate an alphanumeric hash of specified length', () => {
+    const result = generateAlphaNumericHash(5)
+    expect(result).toHaveLength(5)
+  })
+})
+
+describe('getErrorMessage', () => {
+  it('should return an error message from an error object', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: /canary/apps/gitness/src/utils/__tests__/error-utils.test.ts
removed this test since it was duplicated
created_at: 2025-03-03T22:28:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
+    const result = findWaitingDecisions(mockEntriesForWaitingDecisions)
+    expect(result).toEqual([])
+  })
+})
+
+describe('normalizeGitFilePath', () => {
+  it('should normalize file paths with spaces', () => {
+    const result = normalizeGitFilePath('path/with space/file.txt')
+    expect(result).toBe('path/with space/file.txt')
+  })
+})
+
+describe('generateAlphaNumericHash', () => {
+  it('should generate an alphanumeric hash of specified length', () => {
+    const result = generateAlphaNumericHash(5)
+    expect(result).toHaveLength(5)
+  })
+})
+
+describe('getErrorMessage', () => {
+  it('should return an error message from an error object', () => {
+    const error = new Error('Test error')
+    const result = getErrorMessage(error)
+    expect(result).toBe('Test error')
+  })
+})
+
+// Mock data for extractInfoForCodeOwnerContent
+const mockPropsForCodeOwnerContent = {
+  approvedEvaluations: [{ reviewer: { display_name: 'John Doe' } as TypesPrincipalInfo, approved: true }],
+  reqNoChangeReq: false,
+  reqCodeOwnerApproval: true,
+  minApproval: 1,
+  reqCodeOwnerLatestApproval: false,
+  minReqLatestApproval: 0,
+  codeOwnerChangeReqEntries: [],
+  codeOwnerPendingEntries: [],
+  latestCodeOwnerApprovalArr: [],
+  latestApprovalArr: [],
+  codeOwnerApprovalEntries: [
+    {
+      owner_evaluations: [
+        { reviewer: { display_name: 'Jane Smith' } as TypesPrincipalInfo, approved: true } as TypesOwnerEvaluation
+      ]
+    }
+  ],
+  changeReqReviewer: 'John Doe',
+  changeReqEvaluations: []
+}
+
+describe('extractInfoForCodeOwnerContent', () => {
+  it('should extract information for code owner content', () => {
+    const result = extractInfoForCodeOwnerContent(mockPropsForCodeOwnerContent)
+    expect(result).toEqual({
+      title: 'Changes approved',
+      statusMessage: 'Changes were approved by code owners',
+      statusColor: 'text-success',
+      statusIcon: 'success',
+      isNotRequired: false
+    })
+  })
+})
+
+describe('changedFileId', () => {
+  it('should return the changed file ID', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: done
created_at: 2025-03-03T22:32:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: cant actually return false, only outdated or the original review decision that it was passed to
created_at: 2025-03-03T22:37:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: done
created_at: 2025-03-03T22:37:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,253 @@
+import { describe, expect, it } from 'vitest'
+
+import {
+  EnumCheckStatus,
+  EnumPullReqReviewDecision,
+  TypesCodeOwnerEvaluationEntry,
+  TypesOwnerEvaluation,
+  TypesPrincipalInfo
+} from '@harnessio/code-service-client'
+
+import { PullReqReviewDecision, TypeCheckData } from '../../../pages/pull-request/types/types'
+import {
+  capitalizeFirstLetter,
+  changedFileId,
+  checkIfOutdatedSha,
+  determineStatusMessage,
+  extractInfoForCodeOwnerContent,
+  extractInfoFromRuleViolationArr,
+  extractSpecificViolations,
+  findChangeReqDecisions,
+  findWaitingDecisions,
+  generateAlphaNumericHash,
+  generateStatusSummary,
+  getErrorMessage,
+  normalizeGitFilePath,
+  processReviewDecision
+} from '../pull-request-utils'
+
+// Mock data
+const mockChecks = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true, bypassable: false },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false, bypassable: false }
+]
+
+const mockViolationsData = {
+  data: {
+    rule_violations: [
+      { violations: [{ code: 'rule1', description: 'Violation 1' }] },
+      { violations: [{ code: 'rule2', description: 'Violation 2' }] }
+    ]
+  }
+}
+
+const mockReviewDecision = 'approved' as EnumPullReqReviewDecision
+const mockReviewedSHA = 'sha1'
+const mockSourceSHA = 'sha2'
+
+// Mock data for findWaitingDecisions
+const mockEntriesForWaitingDecisions: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approved' }]
+  },
+  {
+    owner_evaluations: [
+      { review_decision: undefined }, // Empty decision
+      { review_decision: 'reviewed' as EnumPullReqReviewDecision }
+    ]
+  }
+]
+
+// Mock data for findChangeReqDecisions
+const mockEntriesForChangeReq: TypesCodeOwnerEvaluationEntry[] = [
+  {
+    owner_evaluations: [
+      { review_decision: 'approve' as EnumPullReqReviewDecision },
+      { review_decision: 'request_changes' as EnumPullReqReviewDecision }
+    ]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'approve' as EnumPullReqReviewDecision }]
+  },
+  {
+    owner_evaluations: [{ review_decision: 'reviewed' as EnumPullReqReviewDecision }]
+  }
+]
+// Mock data for extractInfoFromRuleViolationArr
+const mockRuleViolations = [
+  {
+    violations: [{ message: 'Violation 1' }, { message: 'Violation 2' }]
+  },
+  {
+    violations: [{ message: 'Violation 3' }]
+  }
+]
+
+// Tests
+describe('processReviewDecision', () => {
+  it('should mark review as outdated if SHAs differ', () => {
+    const result = processReviewDecision(mockReviewDecision, mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(PullReqReviewDecision.outdated)
+  })
+})
+
+describe('generateStatusSummary', () => {
+  it('should generate a summary of status checks', () => {
+    const result = generateStatusSummary(mockChecks)
+    expect(result.summary.total).toBe(2)
+    expect(result.summary.successReq).toBe(1)
+    expect(result.summary.failed).toBe(1)
+  })
+})
+
+// Mock data for determineStatusMessage
+const mockChecksForStatusMessage = [
+  { check: { status: 'success' as EnumCheckStatus }, required: true },
+  { check: { status: 'failure' as EnumCheckStatus }, required: false }
+]
+
+describe('determineStatusMessage', () => {
+  it('should determine the status message based on the status summary', () => {
+    const result = determineStatusMessage(mockChecksForStatusMessage as TypeCheckData[])
+    expect(result).toEqual({
+      color: 'text-success',
+      content: '1 failed, 1 succeeded',
+      status: 'success',
+      title: 'All required checks passed'
+    })
+  })
+})
+
+describe('extractSpecificViolations', () => {
+  it('should extract specific violations based on rule', () => {
+    const result = extractSpecificViolations(mockViolationsData, 'rule1')
+    expect(result).toEqual([{ code: 'rule1', description: 'Violation 1' }])
+  })
+})
+
+describe('checkIfOutdatedSha', () => {
+  it('should return true if SHAs differ', () => {
+    const result = checkIfOutdatedSha(mockReviewedSHA, mockSourceSHA)
+    expect(result).toBe(true)
+  })
+})
+
+describe('capitalizeFirstLetter', () => {
+  it('should capitalize the first letter of a string', () => {
+    const result = capitalizeFirstLetter('hello')
+    expect(result).toBe('Hello')
+  })
+})
+
+describe('findChangeReqDecisions', () => {
+  it('should find change request decisions from entries', () => {
+    const result = findChangeReqDecisions(mockEntriesForChangeReq, 'approve')
+    expect(result).toEqual([
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      },
+      {
+        owner_evaluations: [{ review_decision: 'approve' }]
+      }
+    ])
+  })
+})
+
+describe('findWaitingDecisions', () => {
+  it('should find waiting decisions from entries', () => {
path: apps/gitness/src/pages-v2/pull-request/__tests__/pull-request-utils.test.ts
user: login: cjlee01
body: fixed
created_at: 2025-03-03T23:32:47Z
author_association: CONTRIBUTOR

PR comments:
user: login: netlify[bot]
created_at: 2025-03-01T07:40:30Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *harness-xd-review* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 18f6c33f47a38fba7b023c3c5857f87ddba66c12 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/harness-xd-review/deploys/67c63def45611600084c2929 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-1176--harness-xd-review.netlify.app](https://deploy-preview-1176--harness-xd-review.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTExNzYtLWhhcm5lc3MteGQtcmV2aWV3Lm5ldGxpZnkuYXBwIn0.j76vz2mpY-en57Nkq_9oT8fkRzXZ1qcIjJkUOWHZ7Ig)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [harness-xd-review Preview](https://deploy-preview-1176--harness-xd-review.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/harness-xd-review/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: netlify[bot]
created_at: 2025-03-01T07:40:41Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *harness-design* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 18f6c33f47a38fba7b023c3c5857f87ddba66c12 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/harness-design/deploys/67c63def6c07c800088f4e58 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-1176--harness-design.netlify.app](https://deploy-preview-1176--harness-design.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTExNzYtLWhhcm5lc3MtZGVzaWduLm5ldGxpZnkuYXBwIn0.eyPeJ-VDxCs9dsFpl6BPEp0iHYDaLPf2ny7LVupmY_w)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [harness-design Preview](https://deploy-preview-1176--harness-design.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/harness-design/configuration/notifications#deploy-webhooks)._
author_association: NONE