Mocks API Version 1.0 and 1.1

Change-Id: Ic125455e6690343f296c82a2373a9a7f8c5ce86d
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index 0914943..34c7408 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -196,7 +196,7 @@
 
   # API is not yet set - define the default Kustvakt api endpoint
   $conf->{api_path} //= $ENV{KALAMAR_API} || 'https://korap.ids-mannheim.de/api/';
-  $conf->{api_version} //= $API_VERSION;
+  $conf->{api_version} //= $ENV{KALAMAR_API_VERSION} || $API_VERSION;
 
   # Add development path
   if ($self->mode eq 'development') {
diff --git a/t/api.t b/t/api.t
index 5570d0d..8e1dce2 100644
--- a/t/api.t
+++ b/t/api.t
@@ -5,19 +5,21 @@
 our %ENV;
 
 my $app = Test::Mojo->new('Kalamar')->app;
-is($app->korap->api, 'https://korap.ids-mannheim.de/api/v1.0/');
+# Get API version from app config (set in Kalamar.pm startup)
+my $api_version = $app->app->config('Kalamar')->{api_version};
+is($app->korap->api, "https://korap.ids-mannheim.de/api/v".$api_version. "/");
 
 
 $ENV{KALAMAR_API} = 'https://example.com/';
 $app = Test::Mojo->new('Kalamar')->app;
-is($app->korap->api, 'https://example.com/v1.0/');
+is($app->korap->api, "https://example.com/v".$api_version ."/");
 
 $app = Test::Mojo->new('Kalamar' => {
   Kalamar => {
     api_path => 'https://example.org/'
   }
 })->app;
-is($app->korap->api, 'https://example.org/v1.0/');
+is($app->korap->api, "https://example.org/v".$api_version."/");
 
 $ENV{KALAMAR_API} = undef;
 $app = Test::Mojo->new('Kalamar' => {
diff --git a/t/doc.t b/t/doc.t
index b07d3e6..f3a3a7d 100644
--- a/t/doc.t
+++ b/t/doc.t
@@ -10,6 +10,10 @@
   }
 });
 
+# Get API version from app config (set in Kalamar.pm startup)
+my $api_version = $t->app->config('Kalamar')->{api_version};
+
+
 # Bug 2021-06-11
 $t->get_ok('/doc/ql/wildcards?cat=1')
   ->status_is(404) # ! Should be 404!
@@ -125,15 +129,15 @@
 # Check API endpoint
 $t->get_ok('/doc/api' => { 'X-Forwarded-Host' => 'korap.ids-mannheim.de' })
   ->status_is(200)
-  ->text_is('#api-service-uri', 'https://korap.ids-mannheim.de/test/api/v1.0/');
+  ->text_is('#api-service-uri', 'https://korap.ids-mannheim.de/test/api/v' . $api_version . '/');
 
 # Set openapi path
-$app->config('Kalamar')->{openapi} = '/api/v1.0/openapi/';
+$app->config('Kalamar')->{openapi} = '/api/v' . $api_version . '/openapi/';
 
 # Check openapi endpoint
 $t->get_ok('/doc/api' => { 'X-Forwarded-Host' => 'korap.ids-mannheim.de' })
   ->status_is(200)
-  ->element_exists('#openapi > a[href="https://korap.ids-mannheim.de/api/v1.0/openapi/"]');
+  ->element_exists('#openapi > a[href="https://korap.ids-mannheim.de/api/v' . $api_version . '/openapi/"]');
 
 # Languages of dev pages
 $t->get_ok('/doc/development/kalamar')
diff --git a/t/fixtures.t b/t/fixtures.t
index 9816fe6..9dba8cd 100644
--- a/t/fixtures.t
+++ b/t/fixtures.t
@@ -3,29 +3,32 @@
 use Test::Mojo;
 use Mojo::File qw/path/;
 
+# Get API version from Kalamar config (respects env and config file)
+my $api_version = Test::Mojo->new('Kalamar')->app->config('Kalamar')->{api_version};
+
 # Get the fixture path
 my $mock_server = path(Mojo::File->new(__FILE__)->dirname, 'server')->child('mock.pl');
 
 my $t = Test::Mojo->new($mock_server);
 
-$t->get_ok('/v1.0')
+$t->get_ok("/v$api_version")
   ->status_is(200)
-  ->content_is('Fake server available: 1.0');
+  ->content_is("Fake server available: $api_version");
 
-$t->get_ok('/v1.0/search?ql=cosmas3')
+$t->get_ok("/v$api_version/search?ql=cosmas3")
   ->status_is(400)
   ->json_is('/errors/0/0',"307")
   ->json_is('/errors/0/1',"cosmas3 is not a supported query language!")
   ;
 
-my $err = $t->get_ok('/v1.0/search?q=server_fail')
+my $err = $t->get_ok("/v$api_version/search?q=server_fail")
   ->status_is(500)
   ->content_like(qr!Oooops!)
   ->tx->res->dom->at('#error')
   ;
 is(defined $err ? $err->text : '', '');
 
-$err = $t->get_ok('/v1.0/search?q=[orth=das&ql=poliqarp&offset=0&count=25')
+$err = $t->get_ok("/v$api_version/search?q=[orth=das&ql=poliqarp&offset=0&count=25")
   ->status_is(400)
   ->json_is('/errors/0/0',302)
   ->json_is('/errors/0/1','Parantheses/brackets unbalanced.')
@@ -36,7 +39,7 @@
 is(defined $err ? $err->text : '', '');
 
 
-$err = $t->get_ok('/v1.0/search?q=baum&ql=poliqarp&offset=0&count=25')
+$err = $t->get_ok("/v$api_version/search?q=baum&ql=poliqarp&offset=0&count=25")
   ->status_is(200)
   ->json_is('/meta/count', 25)
   ->json_is('/meta/serialQuery', "tokens:s:Baum")
@@ -45,7 +48,7 @@
   ;
 is(defined $err ? $err->text : '', '');
 
-$err = $t->get_ok('/v1.0/search?q=baum&ql=poliqarp&offset=0&count=25&fields=textSigle')
+$err = $t->get_ok("/v$api_version/search?q=baum&ql=poliqarp&offset=0&count=25&fields=textSigle")
   ->status_is(200)
   ->json_is('/meta/count', 25)
   ->json_is('/meta/serialQuery', "tokens:s:Baum")
@@ -56,7 +59,7 @@
 is(defined $err ? $err->text : '', '');
 
 
-$t->get_ok('/v1.0/corpus/WPD15/232/39681/p2133-2134?spans=false&foundry=*')
+$t->get_ok("/v$api_version/corpus/WPD15/232/39681/p2133-2134?spans=false&foundry=*")
   ->status_is(200)
   ->json_is('/textSigle', 'WPD15/232/39681')
   ;
diff --git a/t/plugin/auth-oauth.t b/t/plugin/auth-oauth.t
index f83bd2a..659fe5d 100644
--- a/t/plugin/auth-oauth.t
+++ b/t/plugin/auth-oauth.t
@@ -11,6 +11,11 @@
 my $mount_point = '/realapi/';
 $ENV{KALAMAR_API} = $mount_point;
 
+# Get the fixture path
+my $fixtures_path = path(Mojo::File->new(__FILE__)->dirname, '..', 'server');
+
+my $q = qr!(?:\"|")!;
+
 my $t = Test::Mojo::WithRoles->new('Kalamar' => {
   Kalamar => {
     plugins => ['Auth']
@@ -23,9 +28,9 @@
   }
 });
 
+my $api_version = $t->app->config('Kalamar')->{api_version};
+
 # Mount fake backend
-# Get the fixture path
-my $fixtures_path = path(Mojo::File->new(__FILE__)->dirname, '..', 'server');
 my $fake_backend = $t->app->plugin(
   Mount => {
     $mount_point =>
@@ -97,11 +102,9 @@
   }
 );
 
-my $q = qr!(?:\"|")!;
-
-$t->get_ok('/realapi/v1.0')
+$t->get_ok("/realapi/v$api_version")
   ->status_is(200)
-  ->content_is('Fake server available: 1.0');
+  ->content_is("Fake server available: $api_version");
 
 $t->get_ok('/?q=Baum')
   ->status_is(200)
diff --git a/t/server/mock.pl b/t/server/mock.pl
index fd64d15..1c22624 100644
--- a/t/server/mock.pl
+++ b/t/server/mock.pl
@@ -116,13 +116,13 @@
   $c->render(text => 'Fake server available: ' . $c->stash('apiv'));
 };
 
-get '/v1.0/redirect-target-a' => sub {
+get '/v#apiv/redirect-target-a' => [apiv => ['1.0','1.1']] => sub {
   shift->render(text => 'Redirect Target!');
 } => 'redirect-target';
 
 
 # Base page
-get '/v1.0/redirect' => sub {
+get '/v#apiv/redirect' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   $c->res->code(308);
   $c->res->headers->location($c->url_for('redirect-target')->to_abs);
@@ -131,7 +131,7 @@
 
 
 # Search fixtures
-get '/v1.0/search' => sub {
+get '/v#apiv/search' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->optional('q');
@@ -247,7 +247,7 @@
 };
 
 # Textinfo fixtures
-get '/v1.0/corpus/#corpusId/#docId/#textId' => sub {
+get '/v#apiv/corpus/#corpusId/#docId/#textId' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->optional('response-pipes');
@@ -273,7 +273,7 @@
 
 
 # Matchinfo fixtures
-get '/v1.0/corpus/#corpusId/#docId/#textId/#matchId' => sub {
+get '/v#apiv/corpus/#corpusId/#docId/#textId/#matchId' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->optional('response-pipes');
@@ -300,7 +300,7 @@
 
 
 # Statistics endpoint
-get '/v1.0/statistics' => sub {
+get '/v#apiv/statistics' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->optional('cq');
@@ -329,7 +329,7 @@
 ############
 
 # Request API token
-get '/v1.0/auth/logout' => sub {
+get '/v#apiv/auth/logout' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
 
   if (my $auth = $c->req->headers->header('Authorization')) {
@@ -345,7 +345,7 @@
 
 
 # Request API token
-get '/v1.0/auth/apiToken' => sub {
+get '/v#apiv/auth/apiToken' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
 
   # Get auth header
@@ -391,7 +391,7 @@
 
 
 # Request API token
-post '/v1.0/oauth2/token' => sub {
+post '/v#apiv/oauth2/token' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
 
   my $grant_type = $c->param('grant_type') // 'undefined';
@@ -520,7 +520,7 @@
 };
 
 # Revoke API token
-post '/v1.0/oauth2/revoke' => sub {
+post '/v#apiv/oauth2/revoke' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
 
   my $refresh_token = $c->param('token');
@@ -541,7 +541,7 @@
 };
 
 # Register a client
-post '/v1.0/oauth2/client/register' => sub {
+post '/v#apiv/oauth2/client/register' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $json = $c->req->json;
 
@@ -611,7 +611,7 @@
 };
 
 # Mock API list plugins
-post '/v1.0/plugins' => sub {
+post '/v#apiv/plugins' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->required('super_client_id');
@@ -630,7 +630,7 @@
 };
 
 # Mock API list installed plugins
-post '/v1.0/plugins/installed' => sub {
+post '/v#apiv/plugins/installed' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->required('super_client_id');
@@ -651,7 +651,7 @@
 
 
 # Mock API plugin installation
-post '/v1.0/plugins/install' => sub {
+post '/v#apiv/plugins/install' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->required('super_client_id');
@@ -692,7 +692,7 @@
 };
 
 # Mock API plugin uninstallation
-post '/v1.0/plugins/uninstall' => sub {
+post '/v#apiv/plugins/uninstall' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $v = $c->validation;
   $v->required('super_client_id');
@@ -722,7 +722,7 @@
   };
 
 # Register a client
-post '/v1.0/oauth2/client/list' => sub {
+post '/v#apiv/oauth2/client/list' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
 
   my $v = $c->validation;
@@ -749,7 +749,7 @@
 };
 
 # Get client info
-post '/v1.0/oauth2/client/:client_id' => sub {
+post '/v#apiv/oauth2/client/:client_id' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
 
   # Validate input
@@ -791,7 +791,7 @@
 
 
 # Get token list
-post '/v1.0/oauth2/token/list' => sub {
+post '/v#apiv/oauth2/token/list' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   return $c->render(json => [
     {
@@ -812,7 +812,7 @@
   ]);
 };
 
-del '/v1.0/oauth2/client/deregister/:client_id' => sub {
+del '/v#apiv/oauth2/client/deregister/:client_id' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $client_id = $c->stash('client_id');
 
@@ -846,7 +846,7 @@
   );
 };
 
-get '/v1.0/oauth2/authorize' => sub {
+get '/v#apiv/oauth2/authorize' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $type = $c->param('response_type');
   my $client_id = $c->param('client_id');
@@ -913,7 +913,7 @@
 );
 
 # Store query
-put '/v1.0/query/~:user/:query_name' => sub {
+put '/v#apiv/query/~:user/:query_name' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $user = $c->stash('user');
   my $qname = $c->stash('query_name');
@@ -959,7 +959,7 @@
 };
 
 # Get query
-get '/v1.0/query/~:user/:query_name' => sub {
+get '/v#apiv/query/~:user/:query_name' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
 
   my $user = $c->stash('user');
@@ -986,7 +986,7 @@
 
 
 # Get all queries
-get '/v1.0/query/~:user' => sub {
+get '/v#apiv/query/~:user' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $user = $c->stash('user');
   my $qs = $chi->get('~queries') // [];
@@ -998,8 +998,8 @@
 };
 
 
-# Store query
-del '/v1.0/query/~:user/:query_name' => sub {
+# Delete query
+del '/v#apiv/query/~:user/:query_name' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
   my $user = $c->stash('user');
   my $qname = $c->stash('query_name');
@@ -1021,7 +1021,7 @@
   );
 };
 
-post '/v1.0/oauth2/revoke/super' => sub {
+post '/v#apiv/oauth2/revoke/super' => [apiv => ['1.0','1.1']] => sub {
   my $c = shift;
 
   my $s_client_id = $c->param('super_client_id');