Skip to content

Commit 1a6217a

Browse files
committed
Add unit tests for httpDownload logger and progressInterval options
Tests cover: - Logger with custom progressInterval (25%) - Default progressInterval (10%) - Logger taking precedence over onProgress callback - Progress message format with MB units - No progress logging when Content-Length header is missing All tests use proper Writable streams to capture logger output.
1 parent 91e5db5 commit 1a6217a

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

test/unit/http-request.test.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ import { promises as fs } from 'node:fs'
1515
import http from 'node:http'
1616
import type https from 'node:https'
1717
import path from 'node:path'
18+
import { Writable } from 'node:stream'
1819

1920
import {
2021
httpDownload,
2122
httpGetJson,
2223
httpGetText,
2324
httpRequest,
2425
} from '@socketsecurity/lib/http-request'
26+
import { Logger } from '@socketsecurity/lib/logger'
2527
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
2628
import { runWithTempDir } from './utils/temp-file-helper.mjs'
2729

@@ -691,6 +693,144 @@ describe('http-request', () => {
691693
expect(result.size).toBeGreaterThan(0)
692694
}, 'httpDownload-default-timeout-')
693695
})
696+
697+
it('should log progress with logger option', async () => {
698+
await runWithTempDir(async tmpDir => {
699+
const destPath = path.join(tmpDir, 'logger.txt')
700+
const logMessages: string[] = []
701+
702+
const stdout = new Writable({
703+
write(chunk, _encoding, callback) {
704+
logMessages.push(chunk.toString())
705+
callback()
706+
},
707+
})
708+
709+
const logger = new Logger({ stdout })
710+
711+
await httpDownload(`${httpBaseUrl}/large-download`, destPath, {
712+
logger,
713+
progressInterval: 25, // Log every 25%
714+
})
715+
716+
// Should have logged progress at 25%, 50%, 75%, 100%
717+
expect(logMessages.length).toBeGreaterThan(0)
718+
expect(logMessages.some(msg => msg.includes('Progress:'))).toBe(true)
719+
expect(logMessages.some(msg => msg.includes('MB'))).toBe(true)
720+
721+
const content = await fs.readFile(destPath, 'utf8')
722+
expect(content).toBe('X'.repeat(1000))
723+
}, 'httpDownload-logger-')
724+
})
725+
726+
it('should use default progressInterval of 10%', async () => {
727+
await runWithTempDir(async tmpDir => {
728+
const destPath = path.join(tmpDir, 'logger-default.txt')
729+
const logMessages: string[] = []
730+
731+
const stdout = new Writable({
732+
write(chunk, _encoding, callback) {
733+
logMessages.push(chunk.toString())
734+
callback()
735+
},
736+
})
737+
738+
const logger = new Logger({ stdout })
739+
740+
await httpDownload(`${httpBaseUrl}/large-download`, destPath, {
741+
logger,
742+
// No progressInterval specified - should default to 10%
743+
})
744+
745+
expect(logMessages.length).toBeGreaterThan(0)
746+
expect(logMessages.some(msg => msg.includes('Progress:'))).toBe(true)
747+
}, 'httpDownload-logger-default-')
748+
})
749+
750+
it('should prefer logger over onProgress callback', async () => {
751+
await runWithTempDir(async tmpDir => {
752+
const destPath = path.join(tmpDir, 'logger-precedence.txt')
753+
const logMessages: string[] = []
754+
let onProgressCalled = false
755+
756+
const stdout = new Writable({
757+
write(chunk, _encoding, callback) {
758+
logMessages.push(chunk.toString())
759+
callback()
760+
},
761+
})
762+
763+
const logger = new Logger({ stdout })
764+
765+
await httpDownload(`${httpBaseUrl}/large-download`, destPath, {
766+
logger,
767+
onProgress: () => {
768+
onProgressCalled = true
769+
},
770+
progressInterval: 25,
771+
})
772+
773+
// Logger should have been used
774+
expect(logMessages.length).toBeGreaterThan(0)
775+
// onProgress should NOT have been called
776+
expect(onProgressCalled).toBe(false)
777+
}, 'httpDownload-logger-precedence-')
778+
})
779+
780+
it('should format progress with MB units correctly', async () => {
781+
await runWithTempDir(async tmpDir => {
782+
const destPath = path.join(tmpDir, 'logger-format.txt')
783+
const logMessages: string[] = []
784+
785+
const stdout = new Writable({
786+
write(chunk, _encoding, callback) {
787+
logMessages.push(chunk.toString())
788+
callback()
789+
},
790+
})
791+
792+
const logger = new Logger({ stdout })
793+
794+
await httpDownload(`${httpBaseUrl}/large-download`, destPath, {
795+
logger,
796+
progressInterval: 50,
797+
})
798+
799+
// Check format: " Progress: XX% (Y.Y MB / Z.Z MB)"
800+
expect(logMessages.length).toBeGreaterThan(0)
801+
const progressMsg = logMessages.find(msg => msg.includes('Progress:'))
802+
expect(progressMsg).toBeDefined()
803+
expect(progressMsg).toMatch(
804+
/Progress: \d+% \(\d+\.\d+ MB \/ \d+\.\d+ MB\)/,
805+
)
806+
}, 'httpDownload-logger-format-')
807+
})
808+
809+
it('should not log progress with logger when no content-length', async () => {
810+
await runWithTempDir(async tmpDir => {
811+
const destPath = path.join(tmpDir, 'logger-no-length.txt')
812+
const logMessages: string[] = []
813+
814+
const stdout = new Writable({
815+
write(chunk, _encoding, callback) {
816+
logMessages.push(chunk.toString())
817+
callback()
818+
},
819+
})
820+
821+
const logger = new Logger({ stdout })
822+
823+
await httpDownload(`${httpBaseUrl}/download-no-length`, destPath, {
824+
logger,
825+
})
826+
827+
// Should not have logged any progress (no content-length header)
828+
expect(logMessages.length).toBe(0)
829+
830+
const content = await fs.readFile(destPath, 'utf8')
831+
expect(content).toBe('No content length')
832+
}, 'httpDownload-logger-no-length-')
833+
})
694834
})
695835

696836
describe('httpGetJson', () => {

0 commit comments

Comments
 (0)