/*
* This file is a component of thundr, a software library from 3wks.
* Read more: http://www.3wks.com.au/thundr
* Copyright (C) 2013 3wks, <thundr@3wks.com.au>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.threewks.thundr.bigquery;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import com.atomicleopard.expressive.Expressive;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.services.bigquery.Bigquery;
import com.google.api.services.bigquery.Bigquery.Jobs;
import com.google.api.services.bigquery.Bigquery.Jobs.Insert;
import com.google.api.services.bigquery.model.Job;
import com.google.api.services.bigquery.model.JobConfigurationLoad;
import com.google.api.services.bigquery.model.TableReference;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.TaskHandle;
import com.google.appengine.api.taskqueue.TaskOptions;
public class BigQueryPushServiceImplTest {
private BigQueryPushService bigQueryPushService;
private Queue bigQueryQueue;
private Bigquery bigQuery;
private List<TaskHandle> tasks;
private Insert insert;
private Jobs jobs;
private String bigQueryProjectId = "projectId";
private String bigQueryDatasetId = "datasetId";
private ArgumentCaptor<String> projectIdCaptor = ArgumentCaptor.forClass(String.class);
private ArgumentCaptor<Job> jobCaptor = ArgumentCaptor.forClass(Job.class);
private ArgumentCaptor<ByteArrayContent> contentCaptor = ArgumentCaptor.forClass(ByteArrayContent.class);
@Before
public void before() throws Exception {
bigQuery = mock(Bigquery.class);
bigQueryQueue = mock(Queue.class);
jobs = mock(Jobs.class);
insert = mock(Insert.class);
when(bigQuery.jobs()).thenReturn(jobs);
when(jobs.insert(projectIdCaptor.capture(), jobCaptor.capture(), contentCaptor.capture())).thenReturn(insert);
tasks = new ArrayList<TaskHandle>();
tasks.add(createTestTask(Expressive.<String, String> map("tableId", "table1", "data", "a,b,c"), "task1", "bigQueryQueue"));
tasks.add(createTestTask(Expressive.<String, String> map("tableId", "table1", "data", "d,e,f"), "task2", "bigQueryQueue"));
tasks.add(createTestTask(Expressive.<String, String> map("tableId", "table2", "data", "1,2,3"), "task3", "bigQueryQueue"));
when(bigQueryQueue.leaseTasks(10, TimeUnit.MINUTES, 1000)).thenReturn(tasks);
bigQueryPushService = new BigQueryPushServiceImpl(bigQuery, bigQueryQueue, bigQueryProjectId, bigQueryDatasetId);
}
@Test
public void shouldProcessTasks() throws Exception {
int count = bigQueryPushService.processTasks();
assertThat(count, is(3));
// expect 2 insert() calls - one for table1 and another for table2
verify(insert, times(2)).execute();
List<String> projectIds = projectIdCaptor.getAllValues();
List<Job> jobs = jobCaptor.getAllValues();
List<ByteArrayContent> contents = contentCaptor.getAllValues();
assertThat(projectIds.get(0), is("projectId"));
assertThat(projectIds.get(1), is("projectId"));
assertJob(jobs.get(0), "table1", "datasetId", "projectId");
assertJob(jobs.get(1), "table2", "datasetId", "projectId");
assertContent(contents.get(0), "a,b,c\nd,e,f\n");
assertContent(contents.get(1), "1,2,3\n");
}
@Test
@SuppressWarnings("unchecked")
public void shouldRegisterConverters() {
EventConverter<String> converter = mock(EventConverter.class);
when(converter.getEventClass()).thenReturn(String.class);
when(converter.getCsvData(Mockito.anyString())).thenReturn("1,2,3");
when(converter.getTableId()).thenReturn("tableId");
bigQueryPushService.registerEventConverter(converter);
bigQueryPushService.trackEvent("Test Event");
verify(converter).getEventClass();
verify(converter).getCsvData("Test Event");
verify(converter).getTableId();
}
/**
* Assert that a {@link ByteArrayContent} has the expected content and type.
*
* @param content the {@link ByteArrayContent} to inspect.
* @param value the expected content value.
*/
private void assertContent(ByteArrayContent content, String value) throws Exception {
assertThat(content.getType(), is("application/octet-stream"));
ByteArrayInputStream inputStream = (ByteArrayInputStream) content.getInputStream();
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
assertThat(data, is(value.getBytes()));
}
/**
* Assert that a {@link Job} contains the expected data.
*
* @param job the job to inspect.
* @param tableId the expected table id.
* @param datasetId the expected dataset id.
* @param projectId the expected project id.
*/
private void assertJob(Job job, String tableId, String datasetId, String projectId) {
JobConfigurationLoad load = job.getConfiguration().getLoad();
assertThat(load.getAllowQuotedNewlines(), is(true));
TableReference destinationTable = load.getDestinationTable();
assertThat(destinationTable.getTableId(), is(tableId));
assertThat(destinationTable.getDatasetId(), is(datasetId));
assertThat(destinationTable.getProjectId(), is(projectId));
}
private static TaskHandle createTestTask(Map<String, String> params, String taskName, String queueName) {
TaskOptions options = TaskOptions.Builder.withDefaults();
// build the payload using the parameter map
StringBuilder payload = new StringBuilder();
Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, String> entry = iterator.next();
payload.append(String.format("%s=%s&", entry.getKey(), entry.getValue()));
}
options.taskName(taskName);
options.payload(payload.toString());
TaskHandle task = new TaskHandle(options, queueName);
return task;
}
}