-
Notifications
You must be signed in to change notification settings - Fork 15
[DK TestAI] Add unit tests for requestHandlerLabel #689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,279 @@ | ||
| import { UNNAMED_REQUEST_HANDLER, requestHandlerLabel } from './requestHandlerLabel' | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Evolvability.Organizational] 🔵 SUGGEST The file is placed directly in Action: Move the file to To dismiss: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Functional.Interface] 🔴 BLOCK The test file imports Action: Include To dismiss: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Evolvability.Organizational] 🔵 SUGGEST The test is placed directly in Action: Move the file to To dismiss: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [quality.new-logic-enforcement] 🔵 SUGGEST The quality-ratchet scenario requires that coverage not regress and that new logic ship with tests. This PR is test-only and adds no production code, so no baseline regression is introduced; however, because the module under test is absent the suite cannot execute, meaning the coverage report will not improve and may fail to generate at all. Note that goldenPathGetRules returned no Golden Path rules for this repository/file pattern, so this is reported as a suggestion rather than a block. Action: Land the implementation together with these tests and confirm the coverage run passes and reports a non-negative delta against the baseline before merging. To dismiss: |
||
| describe('requestHandlerLabel', () => { | ||
| describe('UNNAMED_REQUEST_HANDLER constant', () => { | ||
| it('should export the constant with value "undefined"', () => { | ||
| // Arrange & Act & Assert | ||
| expect(UNNAMED_REQUEST_HANDLER).toBe('undefined') | ||
| }) | ||
|
|
||
| it('should be a non-empty string', () => { | ||
| // Arrange & Act & Assert | ||
| expect(typeof UNNAMED_REQUEST_HANDLER).toBe('string') | ||
| expect(UNNAMED_REQUEST_HANDLER.length).toBeGreaterThan(0) | ||
| }) | ||
| }) | ||
|
|
||
| describe('requestHandlerLabel function', () => { | ||
| describe('happy path', () => { | ||
| it('should return the provided requestHandlerName when it is a non-empty string', () => { | ||
| // Arrange | ||
| const handlerName = 'getUserById' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(handlerName) | ||
|
|
||
| // Assert | ||
| expect(result).toBe('getUserById') | ||
| }) | ||
|
|
||
| it('should return the provided requestHandlerName for various valid handler names', () => { | ||
| // Arrange | ||
| const testCases = [ | ||
| 'listUsers', | ||
| 'createPost', | ||
| 'deleteComment', | ||
| 'updateProfile', | ||
| 'builtin:notFound', | ||
| 'builtin:error', | ||
| 'middleware:auth' | ||
| ] | ||
|
|
||
| // Act & Assert | ||
| testCases.forEach(handlerName => { | ||
| expect(requestHandlerLabel(handlerName)).toBe(handlerName) | ||
| }) | ||
| }) | ||
|
|
||
| it('should handle handler names with special characters', () => { | ||
| // Arrange | ||
| const specialNames = [ | ||
| 'handler-with-dash', | ||
| 'handler_with_underscore', | ||
| 'handler.with.dot', | ||
| 'handler/with/slash', | ||
| 'handler:with:colon' | ||
| ] | ||
|
|
||
| // Act & Assert | ||
| specialNames.forEach(name => { | ||
| expect(requestHandlerLabel(name)).toBe(name) | ||
| }) | ||
| }) | ||
|
|
||
| it('should handle handler names with numbers', () => { | ||
| // Arrange | ||
| const nameWithNumbers = 'handler123' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(nameWithNumbers) | ||
|
|
||
| // Assert | ||
| expect(result).toBe('handler123') | ||
| }) | ||
| }) | ||
|
|
||
| describe('edge cases - undefined and null', () => { | ||
| it('should return UNNAMED_REQUEST_HANDLER when requestHandlerName is undefined', () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Evolvability.Textual] 🔵 SUGGEST The describe block is named 'edge cases - undefined and null' but contains no test for a Action: Either add a To dismiss: |
||
| // Arrange | ||
| const handlerName = undefined | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(handlerName) | ||
|
|
||
| // Assert | ||
| expect(result).toBe(UNNAMED_REQUEST_HANDLER) | ||
| expect(result).toBe('undefined') | ||
| }) | ||
|
|
||
| it('should return UNNAMED_REQUEST_HANDLER when no argument is provided', () => { | ||
| // Arrange & Act | ||
| const result = requestHandlerLabel() | ||
|
|
||
| // Assert | ||
| expect(result).toBe(UNNAMED_REQUEST_HANDLER) | ||
| expect(result).toBe('undefined') | ||
| }) | ||
| }) | ||
|
|
||
| describe('edge cases - empty strings', () => { | ||
| it('should return UNNAMED_REQUEST_HANDLER when requestHandlerName is an empty string', () => { | ||
| // Arrange | ||
| const handlerName = '' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(handlerName) | ||
|
|
||
| // Assert | ||
| expect(result).toBe(UNNAMED_REQUEST_HANDLER) | ||
| expect(result).toBe('undefined') | ||
| }) | ||
|
|
||
| it('should fall back for empty string to ensure label is never emitted empty', () => { | ||
| // Arrange | ||
| const emptyHandler = '' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(emptyHandler) | ||
|
|
||
| // Assert | ||
| expect(result).not.toBe('') | ||
| expect(result.length).toBeGreaterThan(0) | ||
| }) | ||
| }) | ||
|
|
||
| describe('edge cases - whitespace-only strings', () => { | ||
| it('should NOT fall back for whitespace-only strings (they are truthy)', () => { | ||
| // Arrange | ||
| const whitespaceHandler = ' ' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(whitespaceHandler) | ||
|
|
||
| // Assert | ||
| expect(result).toBe(' ') | ||
| }) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Functional.Check] 🔵 SUGGEST The test locks in emitting whitespace-only strings (' ', ' ') verbatim as the metric label. Since this value becomes a prom-client label, a whitespace-only handler name produces an invisible, near-undistinguishable time series that will look identical to other whitespace variants in dashboards — the same class of problem the empty-string fallback exists to prevent. Action: Decide whether the fallback should use a trimmed check ( To dismiss: |
||
|
|
||
| it('should preserve single space string', () => { | ||
| // Arrange | ||
| const singleSpace = ' ' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(singleSpace) | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Functional.Check] 🔵 SUGGEST The tests 'should NOT fall back for whitespace-only strings (they are truthy)' and 'should preserve single space string' lock in a whitespace-only value as a valid Prometheus label. The rest of the suite argues the helper exists precisely so the label is never blank; a ' ' label is effectively blank for a human reading a dashboard yet creates a distinct time series, so codifying it as expected behaviour cements a validation gap rather than exposing it. Action: Decide the intended contract explicitly: either trim the input in To dismiss: |
||
| // Assert | ||
| expect(result).toBe(' ') | ||
| }) | ||
| }) | ||
|
|
||
| describe('consistency with aggregation expectations', () => { | ||
| it('should return consistent string for undefined across multiple calls', () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Evolvability.Organizational] 🔵 SUGGEST Several tests duplicate assertions already made elsewhere in the file: 'should preserve historical series identity' (line 178) is the same undefined case as line 77; 'should ensure label is never empty string in any scenario' (line 162) and 'should fall back for empty string...' (line 112) restate line 100; 'should preserve single space string' (line 137) restates line 126; and 'should return a string in all cases' (line 193) is guaranteed by the TypeScript return type. This inflates the suite without adding coverage and raises the cost of every future change to the function. Action: Consolidate the overlapping cases into a single table-driven test (e.g. To dismiss: |
||
| // Arrange & Act | ||
| const result1 = requestHandlerLabel(undefined) | ||
| const result2 = requestHandlerLabel() | ||
| const result3 = requestHandlerLabel('') | ||
|
|
||
| // Assert | ||
| expect(result1).toBe(result2) | ||
| expect(result2).toBe(result3) | ||
| expect(result1).toBe('undefined') | ||
| }) | ||
|
|
||
| it('should ensure label is never empty string in any scenario', () => { | ||
| // Arrange | ||
| const scenarios = [ | ||
| undefined, | ||
| '', | ||
| 'validHandler' | ||
| ] | ||
|
|
||
| // Act & Assert | ||
| scenarios.forEach(scenario => { | ||
| const result = requestHandlerLabel(scenario) | ||
| expect(result).not.toBe('') | ||
| expect(result.length).toBeGreaterThan(0) | ||
| }) | ||
| }) | ||
|
|
||
| it('should preserve historical series identity with "undefined" string', () => { | ||
| // Arrange | ||
| const undefinedHandler = undefined | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(undefinedHandler) | ||
|
|
||
| // Assert | ||
| expect(result).toBe('undefined') | ||
| // Verify it matches the constant to ensure prom-client serialization consistency | ||
| expect(result).toBe(UNNAMED_REQUEST_HANDLER) | ||
| }) | ||
| }) | ||
|
|
||
| describe('type safety', () => { | ||
| it('should return a string in all cases', () => { | ||
| // Arrange | ||
| const testInputs = [undefined, '', 'handler', 'builtin:notFound'] | ||
|
|
||
| // Act & Assert | ||
| testInputs.forEach(input => { | ||
| const result = requestHandlerLabel(input) | ||
| expect(typeof result).toBe('string') | ||
| }) | ||
| }) | ||
|
|
||
| it('should always return UNNAMED_REQUEST_HANDLER or the provided string', () => { | ||
| // Arrange | ||
| const validHandler = 'myHandler' | ||
| const unnamedHandler = undefined | ||
|
|
||
| // Act | ||
| const resultValid = requestHandlerLabel(validHandler) | ||
| const resultUnnamed = requestHandlerLabel(unnamedHandler) | ||
|
|
||
| // Assert | ||
| expect(resultValid === validHandler || resultValid === UNNAMED_REQUEST_HANDLER).toBe(true) | ||
| expect(resultUnnamed === UNNAMED_REQUEST_HANDLER).toBe(true) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Evolvability.SolutionApproach] 🔵 SUGGEST The assertion collapses the comparison to a boolean before passing it to Action: Replace with direct assertions on each case: To dismiss: |
||
| }) | ||
| }) | ||
|
|
||
| describe('long handler names', () => { | ||
| it('should handle very long handler names', () => { | ||
| // Arrange | ||
| const longName = 'a'.repeat(1000) | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(longName) | ||
|
|
||
| // Assert | ||
| expect(result).toBe(longName) | ||
| expect(result.length).toBe(1000) | ||
| }) | ||
| }) | ||
|
|
||
| describe('numeric and special string inputs', () => { | ||
| it('should handle numeric strings', () => { | ||
| // Arrange | ||
| const numericString = '12345' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(numericString) | ||
|
|
||
| // Assert | ||
| expect(result).toBe('12345') | ||
| }) | ||
|
|
||
| it('should handle zero as a string', () => { | ||
| // Arrange | ||
| const zeroString = '0' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(zeroString) | ||
|
|
||
| // Assert | ||
| expect(result).toBe('0') | ||
| }) | ||
|
|
||
| it('should handle the string "false"', () => { | ||
| // Arrange | ||
| const falseString = 'false' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(falseString) | ||
|
|
||
| // Assert | ||
| expect(result).toBe('false') | ||
| }) | ||
|
|
||
| it('should handle the string "null"', () => { | ||
| // Arrange | ||
| const nullString = 'null' | ||
|
|
||
| // Act | ||
| const result = requestHandlerLabel(nullString) | ||
|
|
||
| // Assert | ||
| expect(result).toBe('null') | ||
| }) | ||
| }) | ||
| }) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[general.broken-references] 🔵 SUGGEST
The test imports
UNNAMED_REQUEST_HANDLERandrequestHandlerLabelfrom './requestHandlerLabel', butsrc/service/metrics/requestHandlerLabel.tsdoes not exist on master and is not part of the reviewed diff. If the source module is not included in this PR, the suite will fail to compile under ts-jest.Action: Confirm that
src/service/metrics/requestHandlerLabel.tsis included in this PR (and thatrequestHandlerLabeldeclares its parameter as optional, since line 91 calls it with no argument). Runyarn testlocally to verify the module resolves.To dismiss:
/dk-review dismiss b7e3f1a2-4c58-4d19-9f2e-8a6c0d3b7e51 [reason]