1
1
package edu .unc .lib .boxc .web .common .services ;
2
2
3
3
import edu .unc .lib .boxc .auth .api .services .AccessControlService ;
4
+ import edu .unc .lib .boxc .auth .fcrepo .models .AccessGroupSetImpl ;
5
+ import edu .unc .lib .boxc .fcrepo .exceptions .RangeNotSatisfiableException ;
6
+ import edu .unc .lib .boxc .model .api .DatastreamType ;
7
+ import edu .unc .lib .boxc .model .api .event .PremisLog ;
4
8
import edu .unc .lib .boxc .model .api .exceptions .NotFoundException ;
9
+ import edu .unc .lib .boxc .model .api .ids .PID ;
5
10
import edu .unc .lib .boxc .model .api .objects .BinaryObject ;
6
11
import edu .unc .lib .boxc .model .api .objects .FileObject ;
7
12
import edu .unc .lib .boxc .model .api .objects .RepositoryObjectLoader ;
13
+ import edu .unc .lib .boxc .model .api .rdf .DcElements ;
14
+ import edu .unc .lib .boxc .model .fcrepo .ids .DatastreamPids ;
15
+ import org .apache .jena .rdf .model .ModelFactory ;
8
16
import org .fcrepo .client .FcrepoClient ;
9
17
import org .fcrepo .client .FcrepoOperationFailedException ;
10
18
import org .fcrepo .client .FcrepoResponse ;
19
27
import javax .servlet .http .HttpServletResponse ;
20
28
21
29
import java .io .ByteArrayInputStream ;
22
- import java .io .ByteArrayOutputStream ;
23
30
import java .io .IOException ;
24
31
import java .nio .charset .StandardCharsets ;
25
32
26
33
import static edu .unc .lib .boxc .model .api .DatastreamType .ORIGINAL_FILE ;
27
34
import static edu .unc .lib .boxc .model .fcrepo .test .TestHelper .makePid ;
28
35
import static org .apache .http .HttpHeaders .CONTENT_LENGTH ;
29
- import static org .junit .jupiter .api .Assertions .assertEquals ;
36
+ import static org .apache .http .HttpHeaders .CONTENT_RANGE ;
37
+ import static org .apache .http .HttpHeaders .CONTENT_TYPE ;
38
+ import static org .junit .jupiter .api .Assertions .assertThrows ;
30
39
import static org .mockito .ArgumentMatchers .any ;
31
40
import static org .mockito .ArgumentMatchers .eq ;
41
+ import static org .mockito .Mockito .mock ;
32
42
import static org .mockito .Mockito .verify ;
33
43
import static org .mockito .Mockito .when ;
34
44
import static org .mockito .MockitoAnnotations .openMocks ;
45
+ import static org .springframework .http .HttpHeaders .CONTENT_DISPOSITION ;
35
46
36
47
public class FedoraContentServiceTest {
37
48
private AutoCloseable closeable ;
@@ -54,14 +65,18 @@ public class FedoraContentServiceTest {
54
65
private FcrepoResponse fcrepoResponse ;
55
66
@ Mock
56
67
private ServletOutputStream outputStream ;
68
+ @ Mock
69
+ private PremisLog premisLog ;
57
70
58
71
@ BeforeEach
59
- public void setup () {
72
+ public void setup () throws Exception {
60
73
closeable = openMocks (this );
61
74
fedoraContentService = new FedoraContentService ();
62
75
fedoraContentService .setClient (fcrepoClient );
63
76
fedoraContentService .setAccessControlService (accessControlService );
64
77
fedoraContentService .setRepositoryObjectLoader (repositoryObjectLoader );
78
+ when (response .getOutputStream ()).thenReturn (outputStream );
79
+ when (fileObject .getPremisLog ()).thenReturn (premisLog );
65
80
}
66
81
67
82
@ AfterEach
@@ -90,18 +105,151 @@ public void streamDataWithExternalDatastream() {
90
105
@ Test
91
106
public void streamDataSuccess () throws IOException , FcrepoOperationFailedException {
92
107
var pid = makePid ();
93
- when (repositoryObjectLoader .getFileObject (eq (pid ))).thenReturn (fileObject );
94
- when (fileObject .getOriginalFile ()).thenReturn (binaryObject );
95
- when (binaryObject .getFilename ()).thenReturn ("Best Name" );
96
- when (binaryObject .getPid ()).thenReturn (pid );
97
- when (fcrepoClient .get (any ())).thenReturn (builder );
98
- when (builder .perform ()).thenReturn (fcrepoResponse );
108
+ mockWithOriginalFile (pid );
99
109
when (fcrepoResponse .getBody ()).thenReturn (new ByteArrayInputStream ("image" .getBytes (StandardCharsets .UTF_8 )));
100
- when (response .getOutputStream ()).thenReturn (outputStream );
101
110
when (fcrepoResponse .getHeaderValue (CONTENT_LENGTH )).thenReturn ("5" );
102
111
103
112
fedoraContentService .streamData (pid , ORIGINAL_FILE .getId (), false , response , null );
104
113
105
114
verify (response ).setHeader (CONTENT_LENGTH , "5" );
115
+ verify (response ).setHeader (CONTENT_DISPOSITION , "inline; filename=\" Best Name\" " );
116
+ }
117
+
118
+ @ Test
119
+ public void streamDataSuccessAsAttachment () throws IOException , FcrepoOperationFailedException {
120
+ var pid = makePid ();
121
+ mockWithOriginalFile (pid );
122
+ when (fcrepoResponse .getBody ()).thenReturn (new ByteArrayInputStream ("image" .getBytes (StandardCharsets .UTF_8 )));
123
+ when (fcrepoResponse .getHeaderValue (CONTENT_LENGTH )).thenReturn ("5" );
124
+
125
+ fedoraContentService .streamData (pid , ORIGINAL_FILE .getId (), true , response , null );
126
+
127
+ verify (response ).setHeader (CONTENT_LENGTH , "5" );
128
+ verify (response ).setHeader (CONTENT_DISPOSITION , "attachment; filename=\" Best Name\" " );
129
+ }
130
+
131
+ @ Test
132
+ public void streamDataSuccessOtherDatastream () throws IOException , FcrepoOperationFailedException {
133
+ var pid = makePid ();
134
+ var modsPid = DatastreamPids .getMdDescriptivePid (pid );
135
+ mockWithOriginalFile (pid );
136
+ var modsBinary = mock (BinaryObject .class );
137
+ when (modsBinary .getFilename ()).thenReturn ("mods.xml" );
138
+ when (modsBinary .getPid ()).thenReturn (modsPid );
139
+ when (modsBinary .getFilesize ()).thenReturn (4L );
140
+ when (repositoryObjectLoader .getBinaryObject (eq (modsPid ))).thenReturn (modsBinary );
141
+ when (fcrepoResponse .getBody ()).thenReturn (new ByteArrayInputStream ("desc" .getBytes (StandardCharsets .UTF_8 )));
142
+ when (fcrepoResponse .getHeaderValue (CONTENT_LENGTH )).thenReturn ("4" );
143
+
144
+ fedoraContentService .streamData (pid , DatastreamType .MD_DESCRIPTIVE .getId (), false , response , null );
145
+
146
+ verify (response ).setHeader (CONTENT_LENGTH , "4" );
147
+ verify (response ).setHeader (CONTENT_DISPOSITION , "inline; filename=\" mods.xml\" " );
148
+ }
149
+
150
+ @ Test
151
+ public void streamDataWithValidRange () throws IOException , FcrepoOperationFailedException {
152
+ var pid = makePid ();
153
+ mockWithOriginalFile (pid );
154
+ when (fcrepoResponse .getBody ()).thenReturn (new ByteArrayInputStream ("imag" .getBytes (StandardCharsets .UTF_8 )));
155
+ when (fcrepoResponse .getHeaderValue (CONTENT_LENGTH )).thenReturn ("4" );
156
+ var contentRange = "bytes 0-3/5" ;
157
+ when (fcrepoResponse .getHeaderValue (CONTENT_RANGE )).thenReturn (contentRange );
158
+
159
+ fedoraContentService .streamData (pid , ORIGINAL_FILE .getId (), false , response , "bytes=0-3" );
160
+
161
+ verify (response ).setHeader (CONTENT_LENGTH , "4" );
162
+ verify (response ).setHeader (CONTENT_RANGE , contentRange );
163
+ verify (builder ).addHeader ("Range" , "bytes=0-3" );
164
+ }
165
+
166
+ @ Test
167
+ public void streamDataWithEndRangeSameAsSize () throws IOException , FcrepoOperationFailedException {
168
+ var pid = makePid ();
169
+ mockWithOriginalFile (pid );
170
+ when (fcrepoResponse .getBody ()).thenReturn (new ByteArrayInputStream ("image" .getBytes (StandardCharsets .UTF_8 )));
171
+ when (fcrepoResponse .getHeaderValue (CONTENT_LENGTH )).thenReturn ("5" );
172
+ var contentRange = "bytes 0-4/5" ;
173
+ when (fcrepoResponse .getHeaderValue (CONTENT_RANGE )).thenReturn (contentRange );
174
+
175
+ fedoraContentService .streamData (pid , ORIGINAL_FILE .getId (), false , response , "bytes=0-5" );
176
+
177
+ verify (response ).setHeader (CONTENT_LENGTH , "5" );
178
+ verify (response ).setHeader (CONTENT_RANGE , contentRange );
179
+ verify (builder ).addHeader ("Range" , "bytes=0-" );
180
+ }
181
+
182
+ @ Test
183
+ public void streamDataWithEndRangeGreaterThanSize () throws IOException , FcrepoOperationFailedException {
184
+ var pid = makePid ();
185
+ mockWithOriginalFile (pid );
186
+ when (fcrepoResponse .getBody ()).thenReturn (new ByteArrayInputStream ("mage" .getBytes (StandardCharsets .UTF_8 )));
187
+ when (fcrepoResponse .getHeaderValue (CONTENT_LENGTH )).thenReturn ("4" );
188
+ var contentRange = "bytes 1-4/5" ;
189
+ when (fcrepoResponse .getHeaderValue (CONTENT_RANGE )).thenReturn (contentRange );
190
+
191
+ fedoraContentService .streamData (pid , ORIGINAL_FILE .getId (), false , response , "bytes=1-8" );
192
+
193
+ verify (response ).setHeader (CONTENT_LENGTH , "4" );
194
+ verify (response ).setHeader (CONTENT_RANGE , contentRange );
195
+ verify (builder ).addHeader ("Range" , "bytes=1-" );
196
+ }
197
+
198
+ @ Test
199
+ public void streamDataWithEndRangeInvalid () throws IOException , FcrepoOperationFailedException {
200
+ var pid = makePid ();
201
+ mockWithOriginalFile (pid );
202
+ when (builder .perform ()).thenThrow (new FcrepoOperationFailedException (null , 416 , "Bad Range" ));
203
+ when (fcrepoResponse .getStatusCode ()).thenReturn (416 );
204
+
205
+ assertThrows (RangeNotSatisfiableException .class , () -> {
206
+ fedoraContentService .streamData (pid , ORIGINAL_FILE .getId (), false , response , "bytes=1-bad" );
207
+ });
208
+ }
209
+
210
+ @ Test
211
+ public void streamEventLogTest () throws IOException , FcrepoOperationFailedException {
212
+ var pid = makePid ();
213
+
214
+ when (repositoryObjectLoader .getRepositoryObject (eq (pid ))).thenReturn (fileObject );
215
+ var model = ModelFactory .createDefaultModel ();
216
+ when (premisLog .getEventsModel ()).thenReturn (model );
217
+ var logResource = model .getResource (pid .getRepositoryPath ());
218
+ logResource .addProperty (DcElements .title , "test title" );
219
+
220
+ var principals = new AccessGroupSetImpl ("group" );
221
+
222
+ fedoraContentService .streamEventLog (pid , principals , false , response );
223
+
224
+ verify (response ).setHeader (CONTENT_TYPE , "text/turtle" );
225
+ verify (response ).setHeader (CONTENT_DISPOSITION , "inline; filename=\" " + pid .getId () + "_event_log.ttl\" " );
226
+ }
227
+
228
+ @ Test
229
+ public void streamEventLogAttachmentTest () throws IOException , FcrepoOperationFailedException {
230
+ var pid = makePid ();
231
+ when (repositoryObjectLoader .getRepositoryObject (eq (pid ))).thenReturn (fileObject );
232
+
233
+ var model = ModelFactory .createDefaultModel ();
234
+ when (premisLog .getEventsModel ()).thenReturn (model );
235
+ var logResource = model .getResource (pid .getRepositoryPath ());
236
+ logResource .addProperty (DcElements .title , "test title" );
237
+
238
+ var principals = new AccessGroupSetImpl ("group" );
239
+
240
+ fedoraContentService .streamEventLog (pid , principals , true , response );
241
+
242
+ verify (response ).setHeader (CONTENT_TYPE , "text/turtle" );
243
+ verify (response ).setHeader (CONTENT_DISPOSITION , "attachment; filename=\" " + pid .getId () + "_event_log.ttl\" " );
244
+ }
245
+
246
+ private void mockWithOriginalFile (PID pid ) throws FcrepoOperationFailedException {
247
+ when (repositoryObjectLoader .getFileObject (eq (pid ))).thenReturn (fileObject );
248
+ when (fileObject .getOriginalFile ()).thenReturn (binaryObject );
249
+ when (binaryObject .getFilename ()).thenReturn ("Best Name" );
250
+ when (binaryObject .getPid ()).thenReturn (pid );
251
+ when (binaryObject .getFilesize ()).thenReturn (5L );
252
+ when (fcrepoClient .get (any ())).thenReturn (builder );
253
+ when (builder .perform ()).thenReturn (fcrepoResponse );
106
254
}
107
255
}
0 commit comments