@@ -1882,4 +1882,250 @@ class ManifestMetadataReaderTest {
18821882 fixture.options.sessionReplay.screenshotStrategy,
18831883 )
18841884 }
1885+
1886+ // Network Detail Configuration Tests
1887+
1888+ @Test
1889+ fun `applyMetadata reads comma-separated networkDetailAllowUrls from manifest` () {
1890+ // Arrange
1891+ val expectedUrls = " https://api.example.com/.*,https://cdn.example.com/.*"
1892+ val bundle = bundleOf(ManifestMetadataReader .REPLAYS_NETWORK_DETAIL_ALLOW_URLS to expectedUrls)
1893+ val context = fixture.getContext(metaData = bundle)
1894+
1895+ // Act
1896+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1897+
1898+ // Assert
1899+ val urls = fixture.options.sessionReplay.networkDetailAllowUrls
1900+ assertEquals(2 , urls.size)
1901+ assertEquals(" https://api.example.com/.*" , urls[0 ])
1902+ assertEquals(" https://cdn.example.com/.*" , urls[1 ])
1903+ }
1904+
1905+ @Test
1906+ fun `applyMetadata keeps empty networkDetailAllowUrls when not present` () {
1907+ // Arrange
1908+ val context = fixture.getContext()
1909+
1910+ // Act
1911+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1912+
1913+ // Assert
1914+ assertEquals(0 , fixture.options.sessionReplay.networkDetailAllowUrls.size)
1915+ }
1916+
1917+ @Test
1918+ fun `applyMetadata reads comma-separated networkDetailDenyUrls from manifest` () {
1919+ // Arrange
1920+ val expectedUrls = " https://private.example.com/.*,https://internal.example.com/.*"
1921+ val bundle = bundleOf(ManifestMetadataReader .REPLAYS_NETWORK_DETAIL_DENY_URLS to expectedUrls)
1922+ val context = fixture.getContext(metaData = bundle)
1923+
1924+ // Act
1925+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1926+
1927+ // Assert
1928+ val urls = fixture.options.sessionReplay.networkDetailDenyUrls
1929+ assertEquals(2 , urls.size)
1930+ assertEquals(" https://private.example.com/.*" , urls[0 ])
1931+ assertEquals(" https://internal.example.com/.*" , urls[1 ])
1932+ }
1933+
1934+ @Test
1935+ fun `applyMetadata keeps empty networkDetailDenyUrls when not present` () {
1936+ // Arrange
1937+ val context = fixture.getContext()
1938+
1939+ // Act
1940+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1941+
1942+ // Assert
1943+ assertEquals(0 , fixture.options.sessionReplay.networkDetailDenyUrls.size)
1944+ }
1945+
1946+ @Test
1947+ fun `applyMetadata reads networkCaptureBodies from manifest` () {
1948+ // Arrange
1949+ val bundle = bundleOf(ManifestMetadataReader .REPLAYS_NETWORK_CAPTURE_BODIES to false )
1950+ val context = fixture.getContext(metaData = bundle)
1951+
1952+ // Act
1953+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1954+
1955+ // Assert
1956+ assertFalse(fixture.options.sessionReplay.isNetworkCaptureBodies)
1957+ }
1958+
1959+ @Test
1960+ fun `applyMetadata keeps default networkCaptureBodies as true when not present` () {
1961+ // Arrange
1962+ val context = fixture.getContext()
1963+
1964+ // Act
1965+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1966+
1967+ // Assert
1968+ assertTrue(fixture.options.sessionReplay.isNetworkCaptureBodies)
1969+ }
1970+
1971+ @Test
1972+ fun `applyMetadata keeps the default networkRequestHeaders` () {
1973+ // Arrange
1974+ val context = fixture.getContext()
1975+
1976+ // Act
1977+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1978+
1979+ // Assert
1980+ val headers = fixture.options.sessionReplay.networkRequestHeaders
1981+ val defaultHeaders = SentryReplayOptions .getNetworkDetailsDefaultHeaders()
1982+
1983+ // Should have exactly the default headers
1984+ assertEquals(defaultHeaders.size, headers.size)
1985+ defaultHeaders.forEach { defaultHeader -> assertTrue(headers.contains(defaultHeader)) }
1986+ }
1987+
1988+ @Test
1989+ fun `applyMetadata reads networkRequestHeaders from manifest` () {
1990+ // Arrange
1991+ val expectedHeaders = " Authorization,X-Custom-Header,X-Request-Id"
1992+ val bundle = bundleOf(ManifestMetadataReader .REPLAYS_NETWORK_REQUEST_HEADERS to expectedHeaders)
1993+ val context = fixture.getContext(metaData = bundle)
1994+
1995+ // Act
1996+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1997+
1998+ // Assert
1999+ val allHeaders = fixture.options.sessionReplay.networkRequestHeaders
2000+ val defaultHeaders = SentryReplayOptions .getNetworkDetailsDefaultHeaders()
2001+
2002+ // Should include default headers + additional headers
2003+ defaultHeaders.forEach { defaultHeader ->
2004+ assertTrue(allHeaders.contains(defaultHeader)) // default
2005+ }
2006+ assertTrue(allHeaders.contains(" Authorization" )) // additional
2007+ assertTrue(allHeaders.contains(" X-Custom-Header" )) // additional
2008+ assertTrue(allHeaders.contains(" X-Request-Id" )) // additional
2009+ }
2010+
2011+ @Test
2012+ fun `applyMetadata keeps the default networkResponseHeaders` () {
2013+ // Arrange
2014+ val context = fixture.getContext()
2015+
2016+ // Act
2017+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2018+
2019+ // Assert
2020+ val headers = fixture.options.sessionReplay.networkResponseHeaders
2021+ val defaultHeaders = SentryReplayOptions .getNetworkDetailsDefaultHeaders()
2022+
2023+ // Should have exactly the default headers
2024+ assertEquals(defaultHeaders.size, headers.size)
2025+ defaultHeaders.forEach { defaultHeader -> assertTrue(headers.contains(defaultHeader)) }
2026+ }
2027+
2028+ @Test
2029+ fun `applyMetadata reads networkResponseHeaders from manifest` () {
2030+ // Arrange
2031+ val expectedHeaders = " X-Response-Time,X-Cache-Status,X-Server-Id"
2032+ val bundle =
2033+ bundleOf(ManifestMetadataReader .REPLAYS_NETWORK_RESPONSE_HEADERS to expectedHeaders)
2034+ val context = fixture.getContext(metaData = bundle)
2035+
2036+ // Act
2037+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2038+
2039+ // Assert
2040+ val allHeaders = fixture.options.sessionReplay.networkResponseHeaders
2041+ // Should include default headers + additional headers
2042+ val defaultHeaders = SentryReplayOptions .getNetworkDetailsDefaultHeaders()
2043+ defaultHeaders.forEach { defaultHeader -> assertTrue(allHeaders.contains(defaultHeader)) }
2044+ assertTrue(allHeaders.contains(" X-Response-Time" )) // additional
2045+ assertTrue(allHeaders.contains(" X-Cache-Status" )) // additional
2046+ assertTrue(allHeaders.contains(" X-Server-Id" )) // additional
2047+ }
2048+
2049+ @Test
2050+ fun `applyMetadata skips empty strings for networkDetailAllowUrls and networkDetailDenyUrls` () {
2051+ // Arrange
2052+ val bundle =
2053+ bundleOf(
2054+ ManifestMetadataReader .REPLAYS_NETWORK_DETAIL_ALLOW_URLS to " , " ,
2055+ ManifestMetadataReader .REPLAYS_NETWORK_DETAIL_DENY_URLS to " ,, " ,
2056+ )
2057+ val context = fixture.getContext(metaData = bundle)
2058+
2059+ // Act
2060+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2061+
2062+ // Assert
2063+ assertEquals(0 , fixture.options.sessionReplay.networkDetailAllowUrls.size)
2064+ assertEquals(0 , fixture.options.sessionReplay.networkDetailDenyUrls.size)
2065+ }
2066+
2067+ @Test
2068+ fun `applyMetadata skips empty strings for networkRequestHeaders and networkResponseHeaders` () {
2069+ // Arrange
2070+ val bundle =
2071+ bundleOf(
2072+ ManifestMetadataReader .REPLAYS_NETWORK_REQUEST_HEADERS to " ," ,
2073+ ManifestMetadataReader .REPLAYS_NETWORK_RESPONSE_HEADERS to " ," ,
2074+ )
2075+ val context = fixture.getContext(metaData = bundle)
2076+
2077+ // Act
2078+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2079+
2080+ // Assert
2081+ // Should still have default headers even with empty string
2082+ val defaultHeaders = SentryReplayOptions .getNetworkDetailsDefaultHeaders()
2083+
2084+ val requestHeaders = fixture.options.sessionReplay.networkRequestHeaders
2085+ assertEquals(defaultHeaders.size, requestHeaders.size)
2086+ defaultHeaders.forEach { defaultHeader -> assertTrue(requestHeaders.contains(defaultHeader)) }
2087+
2088+ val responseHeaders = fixture.options.sessionReplay.networkResponseHeaders
2089+ assertEquals(defaultHeaders.size, responseHeaders.size)
2090+ defaultHeaders.forEach { defaultHeader -> assertTrue(responseHeaders.contains(defaultHeader)) }
2091+ }
2092+
2093+ @Test
2094+ fun `applyMetadata trims whitespace from network URLs` () {
2095+ // Arrange
2096+ val bundle =
2097+ bundleOf(
2098+ ManifestMetadataReader .REPLAYS_NETWORK_DETAIL_ALLOW_URLS to
2099+ " https://api.example.com/.* , https://cdn.example.com/.* "
2100+ )
2101+ val context = fixture.getContext(metaData = bundle)
2102+
2103+ // Act
2104+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2105+
2106+ // Assert
2107+ val urls = fixture.options.sessionReplay.networkDetailAllowUrls
2108+ assertEquals(2 , urls.size)
2109+ assertEquals(" https://api.example.com/.*" , urls[0 ])
2110+ assertEquals(" https://cdn.example.com/.*" , urls[1 ])
2111+ }
2112+
2113+ @Test
2114+ fun `applyMetadata trims whitespace from network headers` () {
2115+ // Arrange
2116+ val bundle =
2117+ bundleOf(
2118+ ManifestMetadataReader .REPLAYS_NETWORK_REQUEST_HEADERS to
2119+ " Authorization , X-Custom-Header "
2120+ )
2121+ val context = fixture.getContext(metaData = bundle)
2122+
2123+ // Act
2124+ ManifestMetadataReader .applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2125+
2126+ // Assert
2127+ val headers = fixture.options.sessionReplay.networkRequestHeaders
2128+ assertTrue(headers.contains(" Authorization" ))
2129+ assertTrue(headers.contains(" X-Custom-Header" ))
2130+ }
18852131}
0 commit comments