@Test
public void testPutConnectorConfig() throws Exception {

    EasyMock.expect(member.memberId()).andStubReturn("leader");
    expectRebalance(1, Arrays.asList(CONN1), Collections.<ConnectorTaskId>emptyList());
    expectPostRebalanceCatchup(SNAPSHOT);
    worker.startConnector(EasyMock.<ConnectorConfig>anyObject(), EasyMock.<ConnectorContext>anyObject(),
            EasyMock.eq(herder), EasyMock.eq(TargetState.STARTED));
    PowerMock.expectLastCall();
    EasyMock.expect(worker.isRunning(CONN1)).andReturn(true);
    EasyMock.expect(worker.connectorTaskConfigs(CONN1, MAX_TASKS, null)).andReturn(TASK_CONFIGS);
    // list connectors, get connector info, get connector config, get task configs
    member.wakeup();
    PowerMock.expectLastCall().anyTimes();
    member.poll(EasyMock.anyInt());
    PowerMock.expectLastCall();
    // Poll loop for second round of calls
    member.ensureActive();
    PowerMock.expectLastCall();
    configStorage.putConnectorConfig(CONN1, CONN1_CONFIG_UPDATED);
    PowerMock.expectLastCall().andAnswer(new IAnswer<Object>() {
        @Override
        public Object answer() throws Throwable {
            // Simulate response to writing config + waiting until end of log to be read
            configUpdateListener.onConnectorConfigUpdate(CONN1);
            return null;
        }
    });
    // As a result of reconfig, should need to update snapshot. With only connector updates, we'll just restart
    // connector without rebalance
    EasyMock.expect(configStorage.snapshot()).andReturn(SNAPSHOT_UPDATED_CONN1_CONFIG);
    worker.stopConnector(CONN1);
    PowerMock.expectLastCall();
    Capture<ConnectorConfig> capturedUpdatedConfig = EasyMock.newCapture();
    worker.startConnector(EasyMock.capture(capturedUpdatedConfig), EasyMock.<ConnectorContext>anyObject(),
            EasyMock.eq(herder), EasyMock.eq(TargetState.STARTED));
    PowerMock.expectLastCall();
    EasyMock.expect(worker.isRunning(CONN1)).andReturn(true);
    EasyMock.expect(worker.connectorTaskConfigs(CONN1, MAX_TASKS, null)).andReturn(TASK_CONFIGS);
    member.poll(EasyMock.anyInt());
    PowerMock.expectLastCall();
    // Third tick just to read the config
    member.ensureActive();
    PowerMock.expectLastCall();
    member.poll(EasyMock.anyInt());
    PowerMock.expectLastCall();
    PowerMock.replayAll();
    // Should pick up original config
    FutureCallback<Map<String, String>> connectorConfigCb = new FutureCallback<>();
    herder.connectorConfig(CONN1, connectorConfigCb);
    herder.tick();
    assertTrue(connectorConfigCb.isDone());
    assertEquals(CONN1_CONFIG, connectorConfigCb.get());
    // Apply new config.
    FutureCallback<Herder.Created<ConnectorInfo>> putConfigCb = new FutureCallback<>();
    herder.putConnectorConfig(CONN1, CONN1_CONFIG_UPDATED, true, putConfigCb);
    herder.tick();
    assertTrue(putConfigCb.isDone());
    ConnectorInfo updatedInfo = new ConnectorInfo(CONN1, CONN1_CONFIG_UPDATED, Arrays.asList(TASK0, TASK1, TASK2));
    assertEquals(new Herder.Created<>(false, updatedInfo), putConfigCb.get());
    // Check config again to validate change
    connectorConfigCb = new FutureCallback<>();
    herder.connectorConfig(CONN1, connectorConfigCb);
    herder.tick();
    assertTrue(connectorConfigCb.isDone());
    assertEquals(CONN1_CONFIG_UPDATED, connectorConfigCb.get());
    PowerMock.verifyAll();
}