@@ -29,5 +29,57 @@ public void EnsureStatusCodeStartsAtIs200()
2929 var feature = new InvokeFeatures ( ) as IHttpResponseFeature ;
3030 Assert . Equal ( 200 , feature . StatusCode ) ;
3131 }
32+
33+ // Regression test for https://github.com/aws/aws-lambda-dotnet/issues/1702.
34+ // ASP.NET Core's FeatureReferences cache uses Revision to detect when a
35+ // feature has been swapped (e.g. OutputCache/ResponseCompression replacing
36+ // IHttpResponseBodyFeature to wrap the response body). If Set<TFeature>
37+ // does not bump the revision, cached references stay stale and writes
38+ // bypass the wrapper.
39+ [ Fact ]
40+ public void SetFeatureBumpsRevision ( )
41+ {
42+ IFeatureCollection features = new InvokeFeatures ( ) ;
43+ var initialRevision = features . Revision ;
44+
45+ features . Set < IHttpResponseBodyFeature > ( new TestResponseBodyFeature ( ) ) ;
46+
47+ Assert . NotEqual ( initialRevision , features . Revision ) ;
48+ }
49+
50+ [ Fact ]
51+ public void SetFeatureStoresAndRetrievesInstance ( )
52+ {
53+ IFeatureCollection features = new InvokeFeatures ( ) ;
54+ var replacement = new TestResponseBodyFeature ( ) ;
55+
56+ features . Set < IHttpResponseBodyFeature > ( replacement ) ;
57+
58+ Assert . Same ( replacement , features . Get < IHttpResponseBodyFeature > ( ) ) ;
59+ }
60+
61+ [ Fact ]
62+ public void SetFeatureNullRemovesEntryAndBumpsRevision ( )
63+ {
64+ IFeatureCollection features = new InvokeFeatures ( ) ;
65+ // InvokeFeatures seeds itself as the IHttpResponseBodyFeature in its constructor.
66+ Assert . NotNull ( features . Get < IHttpResponseBodyFeature > ( ) ) ;
67+ var revisionBeforeRemove = features . Revision ;
68+
69+ features . Set < IHttpResponseBodyFeature > ( null ) ;
70+
71+ Assert . Null ( features . Get < IHttpResponseBodyFeature > ( ) ) ;
72+ Assert . NotEqual ( revisionBeforeRemove , features . Revision ) ;
73+ }
74+
75+ private sealed class TestResponseBodyFeature : IHttpResponseBodyFeature
76+ {
77+ public System . IO . Stream Stream => System . IO . Stream . Null ;
78+ public System . IO . Pipelines . PipeWriter Writer => System . IO . Pipelines . PipeWriter . Create ( System . IO . Stream . Null ) ;
79+ public System . Threading . Tasks . Task CompleteAsync ( ) => System . Threading . Tasks . Task . CompletedTask ;
80+ public void DisableBuffering ( ) { }
81+ public System . Threading . Tasks . Task SendFileAsync ( string path , long offset , long ? count , System . Threading . CancellationToken cancellationToken ) => System . Threading . Tasks . Task . CompletedTask ;
82+ public System . Threading . Tasks . Task StartAsync ( System . Threading . CancellationToken cancellationToken ) => System . Threading . Tasks . Task . CompletedTask ;
83+ }
3284 }
3385}
0 commit comments