// =================================================================================================
// Copyright 2011 Twitter, Inc.
// -------------------------------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this work except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.net;
import com.twitter.common.base.ExceptionalFunction;
import com.twitter.common.net.UrlResolver.ResolvedUrl;
import com.twitter.common.net.UrlResolver.ResolvedUrl.EndState;
import com.twitter.common.util.BackoffStrategy;
import com.twitter.common.util.Clock;
import org.easymock.IMocksControl;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import static com.google.common.testing.junit4.JUnitAsserts.assertContentsInOrder;
import static org.easymock.EasyMock.createControl;
import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* @author John Sirois
*/
public class UrlResolverTest {
private IMocksControl control;
private ExceptionalFunction<String, String, IOException> resolver;
private Clock clock;
private BackoffStrategy backoffStrategy;
@Before
public void setUp() throws Exception {
control = createControl();
@SuppressWarnings("unchecked")
ExceptionalFunction<String, String, IOException> resolver =
control.createMock(ExceptionalFunction.class);
this.resolver = resolver;
this.clock = control.createMock(Clock.class);
this.backoffStrategy = control.createMock(BackoffStrategy.class);
}
@Test
public void testResolveUrlResolved() throws Exception {
expect(resolver.apply("jake")).andReturn("jake");
control.replay();
ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
assertEquals("jake", resolvedUrl.getStartUrl());
assertContentsInOrder("Expected no intermediate urls", resolvedUrl.getIntermediateUrls());
assertNull(resolvedUrl.getEndUrl());
assertEquals(EndState.REACHED_LANDING, resolvedUrl.getEndState());
control.verify();
}
@Test
public void testResolveUrlSingleRedirect() throws Exception {
expect(resolver.apply("jake")).andReturn("joe");
expect(resolver.apply("joe")).andReturn("joe");
control.replay();
ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
assertEquals("jake", resolvedUrl.getStartUrl());
assertContentsInOrder("Expected no intermediate urls", resolvedUrl.getIntermediateUrls());
assertEquals("joe", resolvedUrl.getEndUrl());
assertEquals(EndState.REACHED_LANDING, resolvedUrl.getEndState());
control.verify();
}
@Test
public void testResolveUrlMultipleRedirects() throws Exception {
expect(resolver.apply("jake")).andReturn("joe");
expect(resolver.apply("joe")).andReturn("bill");
expect(resolver.apply("bill")).andReturn("bob");
expect(resolver.apply("bob")).andReturn("fred");
expect(resolver.apply("fred")).andReturn("fred");
control.replay();
ResolvedUrl resolvedUrl = createResolver(5).resolveUrl("jake");
assertEquals("jake", resolvedUrl.getStartUrl());
assertContentsInOrder(resolvedUrl.getIntermediateUrls(), "joe", "bill", "bob");
assertEquals("fred", resolvedUrl.getEndUrl());
assertEquals(EndState.REACHED_LANDING, resolvedUrl.getEndState());
control.verify();
}
@Test
public void testResolveUrlRedirectLimit() throws Exception {
expect(resolver.apply("jake")).andReturn("joe");
expect(resolver.apply("joe")).andReturn("bill");
control.replay();
ResolvedUrl resolvedUrl = createResolver(2).resolveUrl("jake");
assertEquals("jake", resolvedUrl.getStartUrl());
assertContentsInOrder(resolvedUrl.getIntermediateUrls(), "joe");
assertEquals("bill", resolvedUrl.getEndUrl());
assertEquals(EndState.REDIRECT_LIMIT, resolvedUrl.getEndState());
control.verify();
}
@Test
public void testResolveUrlResolveError() throws Exception {
expect(resolver.apply("jake")).andThrow(new IOException());
control.replay();
ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
assertEquals("jake", resolvedUrl.getStartUrl());
assertContentsInOrder("Expected no intermediate urls", resolvedUrl.getIntermediateUrls());
assertNull(resolvedUrl.getEndUrl());
assertEquals(EndState.ERROR, resolvedUrl.getEndState());
control.verify();
}
@Test
public void testResolveUrlResolveErrorCode() throws Exception {
expect(resolver.apply("jake")).andReturn(null);
expect(backoffStrategy.calculateBackoffMs(0L)).andReturn(1L);
clock.waitFor(1L);
expect(resolver.apply("jake")).andReturn(null);
expect(backoffStrategy.calculateBackoffMs(1L)).andReturn(2L);
clock.waitFor(2L);
expect(resolver.apply("jake")).andReturn(null);
// we shouldn't back off after the last attempt
control.replay();
ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
assertEquals("jake", resolvedUrl.getStartUrl());
assertContentsInOrder("Expected no intermediate urls", resolvedUrl.getIntermediateUrls());
assertNull(resolvedUrl.getEndUrl());
assertEquals(EndState.ERROR, resolvedUrl.getEndState());
control.verify();
}
@Test
public void testResolveStepsToPermanentError() throws Exception {
expect(resolver.apply("jake")).andReturn("joe");
expect(resolver.apply("joe")).andReturn("fred");
expect(resolver.apply("fred")).andReturn(null);
// we shouldn't back off after the last attempt
control.replay();
ResolvedUrl resolvedUrl = createResolver(3).resolveUrl("jake");
assertEquals("jake", resolvedUrl.getStartUrl());
assertContentsInOrder(resolvedUrl.getIntermediateUrls(), "joe");
assertEquals("fred", resolvedUrl.getEndUrl());
assertEquals(EndState.ERROR, resolvedUrl.getEndState());
}
private UrlResolver createResolver(int maxRedirects) {
return new UrlResolver(clock, backoffStrategy, resolver, maxRedirects);
}
}