View Javadoc
1   /*
2    * Copyright (c) 2002-2025, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.portal.service.search;
35  
36  import java.io.File;
37  import java.io.FileInputStream;
38  import java.io.FileOutputStream;
39  import java.io.IOException;
40  import java.io.InputStream;
41  import java.io.OutputStream;
42  import java.util.Arrays;
43  import java.util.List;
44  import java.util.Properties;
45  
46  import org.apache.lucene.document.Document;
47  import org.apache.lucene.document.Field;
48  import org.apache.lucene.document.FieldType;
49  import org.apache.lucene.document.StringField;
50  import org.apache.lucene.document.TextField;
51  import org.apache.lucene.index.IndexWriter;
52  import org.apache.lucene.index.IndexWriterConfig;
53  import org.apache.lucene.index.IndexWriterConfig.OpenMode;
54  import org.apache.lucene.store.Directory;
55  import org.springframework.mock.web.MockHttpServletRequest;
56  
57  import fr.paris.lutece.portal.business.page.Page;
58  import fr.paris.lutece.portal.service.init.LuteceInitException;
59  import fr.paris.lutece.portal.service.security.LuteceUser;
60  import fr.paris.lutece.portal.service.security.MokeLuteceAuthentication;
61  import fr.paris.lutece.portal.service.security.SecurityService;
62  import fr.paris.lutece.portal.service.spring.SpringContextService;
63  import fr.paris.lutece.portal.service.util.AppPropertiesService;
64  import fr.paris.lutece.test.LuteceTestCase;
65  
66  /**
67   * Test the LuceneSearchEngine class
68   */
69  public class LuceneSearchEngineTest extends LuteceTestCase
70  {
71  
72      private static final String BEAN_SEARCH_ENGINE = "searchEngine";
73  
74      private static boolean firstRun = true;
75      private static SearchEngine _engine;
76  
77      /* mimic initialization in IndexationService.processIndexing */
78      private IndexWriter getIndexWriter( ) throws Exception
79      {
80          Directory dir = IndexationService.getDirectoryIndex( );
81          IndexWriterConfig conf = new IndexWriterConfig( IndexationService.getAnalyser( ) );
82          conf.setOpenMode( OpenMode.CREATE );
83          return new IndexWriter( dir, conf );
84      }
85  
86      @Override
87      protected void setUp( ) throws Exception
88      {
89          super.setUp( );
90          if ( firstRun )
91          {
92              firstRun = false;
93  
94              _engine = SpringContextService.getBean( BEAN_SEARCH_ENGINE );
95  
96              FieldType ft = new FieldType( StringField.TYPE_STORED );
97              ft.setOmitNorms( false );
98              Document doc = new Document( );
99              doc.add( new Field( SearchItem.FIELD_DATE, "2014-06-06", ft ) );
100             doc.add( new Field( SearchItem.FIELD_CONTENTS, "lutecefoo lutecebar", TextField.TYPE_NOT_STORED ) );
101             doc.add( new StringField( SearchItem.FIELD_METADATA, "lutecetag", Field.Store.NO ) );
102 
103             doc.add( new Field( SearchItem.FIELD_TYPE, "lutecetype", ft ) );
104             doc.add( new Field( SearchItem.FIELD_ROLE, "role1", ft ) );
105 
106             // Not using IndexationService.write(doc) because it needs to be
107             // called by IndexationService.processIndexing() (or else it throws null pointer exception)
108             IndexWriter indexWriter = getIndexWriter( );
109             indexWriter.addDocument( doc );
110 
111             doc = new Document( );
112             doc.add( new Field( SearchItem.FIELD_CONTENTS, "lutecebaz", TextField.TYPE_NOT_STORED ) );
113             doc.add( new Field( SearchItem.FIELD_ROLE, Page.ROLE_NONE, ft ) );
114             indexWriter.addDocument( doc );
115 
116             indexWriter.close( );
117         }
118     }
119 
120     public void testSearchSimpleMatch( ) throws Exception
121     {
122         MockHttpServletRequest request = new MockHttpServletRequest( );
123         List<SearchResult> listResults = _engine.getSearchResults( "lutecefoo", request );
124         assertTrue( "The search results list should have one element. Got : " + listResults, listResults != null && listResults.size( ) == 1 );
125     }
126 
127     public void testSearchSimpleNoMatch( ) throws Exception
128     {
129         MockHttpServletRequest request = new MockHttpServletRequest( );
130         List<SearchResult> listResults = _engine.getSearchResults( "lutecebadfoo", request );
131         assertTrue( "The search results list should have no elements. Got : " + listResults, listResults != null && listResults.size( ) == 0 );
132     }
133 
134     public void testSearchDateMatch( ) throws Exception
135     {
136         MockHttpServletRequest request = new MockHttpServletRequest( );
137         request.setParameter( "date_after", "01/01/2014" );
138         request.setParameter( "date_before", "01/10/2015" );
139         List<SearchResult> listResults = _engine.getSearchResults( "lutecefoo", request );
140         assertTrue( "The search results list should have one element. Got : " + listResults, listResults != null && listResults.size( ) == 1 );
141     }
142 
143     public void testSearchDateNoMatch( ) throws Exception
144     {
145         MockHttpServletRequest request = new MockHttpServletRequest( );
146         request.setParameter( "date_after", "01/01/2010" );
147         request.setParameter( "date_before", "01/10/2011" );
148         List<SearchResult> listResults = _engine.getSearchResults( "lutecefoo", request );
149         assertTrue( "The search results list should have no elements. Got : " + listResults, listResults != null && listResults.size( ) == 0 );
150     }
151 
152     public void testSearchTypeMatch( ) throws Exception
153     {
154         MockHttpServletRequest request = new MockHttpServletRequest( );
155         request.setParameter( "type_filter", "lutecetype" );
156         List<SearchResult> listResults = _engine.getSearchResults( "lutecefoo", request );
157         assertTrue( "The search results list should have one element. Got : " + listResults, listResults != null && listResults.size( ) == 1 );
158     }
159 
160     public void testSearchTypeNoMatch( ) throws Exception
161     {
162         MockHttpServletRequest request = new MockHttpServletRequest( );
163         request.setParameter( "type_filter", "lutecebadtype" );
164         List<SearchResult> listResults = _engine.getSearchResults( "lutecefoo", request );
165         assertTrue( "The search results list should have no elements. Got : " + listResults, listResults != null && listResults.size( ) == 0 );
166     }
167 
168     public void testSearchTagMatch( ) throws Exception
169     {
170         MockHttpServletRequest request = new MockHttpServletRequest( );
171         request.setParameter( "tag_filter", "lutecetag" );
172         List<SearchResult> listResults = _engine.getSearchResults( "lutecetag", request );
173         assertTrue( "The search results list should have one element. Got : " + listResults, listResults != null && listResults.size( ) == 1 );
174     }
175 
176     public void testSearchTagNoMatch( ) throws Exception
177     {
178         MockHttpServletRequest request = new MockHttpServletRequest( );
179         request.setParameter( "tag_filter", "lutecetag" );
180         List<SearchResult> listResults = _engine.getSearchResults( "lutecebadtag", request );
181         assertTrue( "The search results list should have no elements. Got : " + listResults, listResults != null && listResults.size( ) == 0 );
182     }
183 
184     public void testSearchUserMatch( ) throws Exception
185     {
186 
187         // XXX copy pasted from PortalMenuServiceTest
188         boolean authStatus;
189         authStatus = enableAuthentication( );
190         try
191         {
192             LuteceUser user = new LuteceUser( "junit", SecurityService.getInstance( ).getAuthenticationService( ) )
193             {
194             };
195 
196             user.setRoles( Arrays.asList( "role1" ) );
197             MockHttpServletRequest request = new MockHttpServletRequest( );
198             request.getSession( ).setAttribute( "lutece_user", user );
199 
200             List<SearchResult> listResults = _engine.getSearchResults( "lutecefoo", request );
201             assertTrue( "The search results list should have one element. Got : " + listResults, listResults != null && listResults.size( ) == 1 );
202         }
203         finally
204         {
205             restoreAuthentication( authStatus );
206         }
207     }
208 
209     public void testSearchUserNoMatch( ) throws Exception
210     {
211 
212         // XXX copy pasted from PortalMenuServiceTest
213         boolean authStatus;
214         authStatus = enableAuthentication( );
215         try
216         {
217             LuteceUser user = new LuteceUser( "junit", SecurityService.getInstance( ).getAuthenticationService( ) )
218             {
219             };
220 
221             user.setRoles( Arrays.asList( "role2" ) );
222             MockHttpServletRequest request = new MockHttpServletRequest( );
223             request.getSession( ).setAttribute( "lutece_user", user );
224 
225             List<SearchResult> listResults = _engine.getSearchResults( "lutecefoo", request );
226             assertTrue( "The search results list should have no elements. Got : " + listResults, listResults != null && listResults.size( ) == 0 );
227         }
228         finally
229         {
230             restoreAuthentication( authStatus );
231         }
232     }
233 
234     public void testSearchUserNoMatchNoUser( ) throws Exception
235     {
236 
237         // XXX copy pasted from PortalMenuServiceTest
238         boolean authStatus;
239         authStatus = enableAuthentication( );
240         try
241         {
242             MockHttpServletRequest request = new MockHttpServletRequest( );
243 
244             List<SearchResult> listResults = _engine.getSearchResults( "lutecebadfoo", request );
245             assertTrue( "The search results list should have no elements. Got : " + listResults, listResults != null && listResults.size( ) == 0 );
246         }
247         finally
248         {
249             restoreAuthentication( authStatus );
250         }
251     }
252 
253     // /XXX refactor, this is copy pasted from PortalMenuServiceTest
254     private void restoreAuthentication( boolean status ) throws IOException, LuteceInitException
255     {
256         if ( !status )
257         {
258             File luteceProperties = new File( getResourcesDir( ), "WEB-INF/conf/lutece.properties" );
259             Properties props = new Properties( );
260             InputStream is = new FileInputStream( luteceProperties );
261             props.load( is );
262             is.close( );
263             props.remove( "mylutece.authentication.enable" );
264             props.remove( "mylutece.authentication.class" );
265 
266             OutputStream os = new FileOutputStream( luteceProperties );
267             props.store( os, "saved for junit " + this.getClass( ).getCanonicalName( ) );
268             os.close( );
269             AppPropertiesService.reloadAll( );
270             SecurityService.init( );
271         }
272     }
273 
274     // /XXX refactor, this is copy pasted from PortalMenuServiceTest
275     private boolean enableAuthentication( ) throws IOException, LuteceInitException
276     {
277         boolean status = SecurityService.isAuthenticationEnable( );
278 
279         if ( !status )
280         {
281             File luteceProperties = new File( getResourcesDir( ), "WEB-INF/conf/lutece.properties" );
282             Properties props = new Properties( );
283             InputStream is = new FileInputStream( luteceProperties );
284             props.load( is );
285             is.close( );
286             props.setProperty( "mylutece.authentication.enable", "true" );
287             props.setProperty( "mylutece.authentication.class", MokeLuteceAuthentication.class.getName( ) );
288 
289             OutputStream os = new FileOutputStream( luteceProperties );
290             props.store( os, "saved for junit " + this.getClass( ).getCanonicalName( ) );
291             os.close( );
292             AppPropertiesService.reloadAll( );
293             SecurityService.init( );
294         }
295 
296         return status;
297     }
298 }