View Javadoc
1   package net.sf.okapi.connectors.google.v3;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertFalse;
5   import static org.junit.jupiter.api.Assertions.assertNotNull;
6   import static org.junit.jupiter.api.Assertions.assertTrue;
7   
8   import java.util.List;
9   
10  import org.junit.jupiter.api.AfterEach;
11  import org.junit.jupiter.api.BeforeEach;
12  import org.junit.jupiter.api.DisplayName;
13  import org.junit.jupiter.api.Test;
14  import org.junit.jupiter.api.condition.EnabledIf;
15  
16  import com.acumenvelocity.ath.common.Const;
17  
18  import net.sf.okapi.common.LocaleId;
19  import net.sf.okapi.common.query.QueryResult;
20  import net.sf.okapi.common.resource.TextFragment;
21  import net.sf.okapi.common.resource.TextFragment.TagType;
22  
23  /**
24   * Integration tests for GoogleMTv3Connector using the real Google Cloud Translation API v3.
25   * Requires valid credentials via environment variables:
26   * - ATH_GT_API_KEY (API key) OR
27   * - ATH_GCS_SECRET_FILE (path to service account JSON)
28   * Project ID is fixed to "ath-okapi"
29   */
30  @DisplayName("GoogleMTv3Connector Integration Tests")
31  @EnabledIf("isCredentialsAvailable")
32  class TestGoogleMTv3Connector_IT {
33  
34    private GoogleMTv3Connector connector;
35  
36    /** Custom condition – enables the entire test class only if credentials are configured */
37    static boolean isCredentialsAvailable() {
38      return System.getenv("ATH_GT_API_KEY") != null || System.getenv("ATH_GCS_SECRET_FILE") != null;
39    }
40  
41    @BeforeEach
42    void setUp() {
43      String apiKey = System.getenv("ATH_GT_API_KEY");
44      String credentialsPath = System.getenv("ATH_GCS_SECRET_FILE");
45  
46      GoogleMTv3Parameters params = new GoogleMTv3Parameters();
47      
48      params.setProjectLocation(Const.US_CENTRAL1_PROJECT_LOCATION);
49      if (apiKey != null && !apiKey.isBlank()) {
50        params.setApiKey(apiKey);
51      }
52      if (credentialsPath != null && !credentialsPath.isBlank()) {
53        params.setCredentialsPath(credentialsPath);
54      }
55      params.setMimeType("text/plain");
56  
57      connector = new GoogleMTv3Connector();
58      connector.setParameters(params);
59      connector.setLanguages(LocaleId.ENGLISH, LocaleId.SPANISH);
60      connector.open();
61    }
62  
63    @AfterEach
64    void tearDown() {
65      if (connector != null) {
66        connector.close();
67      }
68    }
69  
70    @Test
71    @DisplayName("Basic plain text translation EN → ES")
72    void testPlainTextTranslation() {
73      connector.setLanguages(LocaleId.ENGLISH, LocaleId.SPANISH);
74  
75      int count = connector.query("Hello, world!");
76  
77      assertEquals(1, count);
78      assertTrue(connector.hasNext());
79  
80      QueryResult qr = connector.next();
81      assertNotNull(qr);
82      assertEquals(100, qr.getFuzzyScore());
83      assertEquals(net.sf.okapi.common.query.MatchType.MT, qr.matchType);
84  
85      String translated = qr.target.getCodedText();
86      assertFalse(translated.equals("Hello, world!"), "Translation should be different from source");
87      assertTrue(
88          translated.toLowerCase().contains("hola") || translated.toLowerCase().contains("mundo"),
89          "Expected Spanish translation, got: " + translated);
90    }
91  
92    @Test
93    @DisplayName("Coded text translation with inline tags preserved (EN → FR)")
94    void testCodedTextTranslation() {
95      connector.setLanguages(LocaleId.ENGLISH, LocaleId.FRENCH);
96  
97      TextFragment src = new TextFragment();
98      src.append("Click the ");
99      src.append(TagType.OPENING, "b", "<b>");
100     src.append("Save");
101     src.append(TagType.CLOSING, "b", "</b>");
102     src.append(" button to continue.");
103 
104     int count = connector.query(src);
105 
106     assertEquals(1, count);
107     assertTrue(connector.hasNext());
108 
109     QueryResult qr = connector.next();
110     assertNotNull(qr);
111     assertEquals(100, qr.getFuzzyScore());
112 
113     // Check that codes are preserved
114     assertTrue(qr.target.hasCode(), "Target should have codes");
115 
116     String targetText = qr.target.toText();
117     // Should contain French words
118     assertTrue(targetText.toLowerCase().contains("bouton") ||
119         targetText.toLowerCase().contains("enregistrer") ||
120         targetText.toLowerCase().contains("sauvegarder") ||
121         targetText.toLowerCase().contains("cliqu"),
122         "Expected French translation: " + targetText);
123   }
124 
125   @Test
126   @DisplayName("Language detection works (source = und → DE)")
127   void testLanguageDetection() {
128     connector.setLanguages(LocaleId.EMPTY, LocaleId.GERMAN);
129 
130     int count = connector.query("This is a test sentence in English.");
131 
132     assertEquals(1, count);
133     assertTrue(connector.hasNext());
134 
135     QueryResult qr = connector.next();
136     assertNotNull(qr);
137 
138     String translated = qr.target.getCodedText().toLowerCase();
139     assertTrue(
140         translated.contains("ist") || translated.contains("test") || translated.contains("satz"),
141         "Should be German: " + translated);
142   }
143 
144   @Test
145   @DisplayName("getSupportedLanguages returns a large list")
146   void testGetSupportedLanguages() {
147     List<LocaleId> languages = connector.getSupportedLanguages();
148 
149     assertNotNull(languages);
150     assertTrue(languages.size() > 130,
151         "Google Translate v3 supports >130 languages, got " + languages.size());
152     assertTrue(languages.contains(LocaleId.fromBCP47("en")));
153     assertTrue(languages.contains(LocaleId.fromBCP47("es")));
154     assertTrue(languages.contains(LocaleId.fromBCP47("zh-CN")));
155     assertTrue(languages.contains(LocaleId.fromBCP47("ja")));
156   }
157 
158   @Test
159   @DisplayName("Connector metadata is correct")
160   void testConnectorMetadata() {
161     assertEquals("Google-MTv3", connector.getName());
162     String settings = connector.getSettingsDisplay();
163     assertTrue(settings.contains(Const.US_CENTRAL1_PROJECT_LOCATION));
164   }
165 
166   @Test
167   @DisplayName("Empty query returns 0 and no results")
168   void testEmptyQuery() {
169     int count = connector.query("");
170     assertEquals(0, count);
171     assertFalse(connector.hasNext());
172   }
173 
174   @Test
175   @DisplayName("Very long text is handled correctly (batching)")
176   void testLongTextBatching() {
177     connector.setLanguages(LocaleId.ENGLISH, LocaleId.JAPANESE);
178 
179     // Create a long text > 30k codepoints to force batching
180     StringBuilder sb = new StringBuilder();
181     
182     for (int i = 0; i < 400; i++) {
183       sb.append("This is a test sentence number ").append(i).append(". ");
184     }
185     
186     String longText = sb.toString();
187 
188     int count = connector.query(longText);
189 
190     assertTrue(count > 0);
191     assertTrue(connector.hasNext());
192 
193     QueryResult qr = connector.next();
194     assertNotNull(qr);
195     String translated = qr.target.getCodedText();
196     assertFalse(translated.isBlank());
197     assertFalse(translated.equals(longText), "Should be translated to Japanese");
198   }
199 
200   @Test
201   @DisplayName("Batch query with multiple fragments")
202   void testBatchQuery() {
203     connector.setLanguages(LocaleId.ENGLISH, LocaleId.SPANISH);
204 
205     List<String> texts = List.of(
206         "Hello",
207         "Goodbye",
208         "Thank you");
209 
210     List<List<QueryResult>> results = connector.batchQueryText(texts);
211 
212     assertNotNull(results);
213     assertEquals(3, results.size());
214 
215     for (List<QueryResult> resultList : results) {
216       assertEquals(1, resultList.size());
217       QueryResult qr = resultList.get(0);
218       assertNotNull(qr.target);
219       assertEquals(100, qr.getFuzzyScore());
220     }
221   }
222 }