// Copyright 2018 Google LLC
//
// 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
//
//     https://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.

syntax = "proto3";

import "google/api/annotations.proto";
import "google/api/routing.proto";
import "google/api/client.proto";
import "google/api/resource.proto";
import "google/protobuf/empty.proto";

package google.showcase.v1beta1;

option go_package = "github.com/googleapis/gapic-showcase/server/genproto";
option java_package = "com.google.showcase.v1beta1";
option java_multiple_files = true;

// A service to facilitate running discrete sets of tests
// against Showcase.
service Testing {
  // This service is meant to only run locally on the port 7469 (keypad digits
  // for "show").
  option (google.api.default_host) = "localhost:7469";
  option (google.api.oauth_scopes) =
      "https://www.googleapis.com/auth/cloud-platform";

  // Creates a new testing session.
  rpc CreateSession(CreateSessionRequest) returns (Session) {
    option (google.api.http) = {
      post: "/v1beta1/sessions"
      body: "session"
    };
    option (google.api.method_signature) = "session,blueprint";
  }

  // Gets a testing session.
  rpc GetSession(GetSessionRequest) returns (Session) {
    option (google.api.http) = {
      get: "/v1beta1/{name=sessions/*}"
    };
  }

  // Lists the current test sessions.
  rpc ListSessions(ListSessionsRequest) returns (ListSessionsResponse) {
    option (google.api.http) = {
      get: "/v1beta1/sessions"
    };
  }

  // Delete a test session.
  rpc DeleteSession(DeleteSessionRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1beta1/{name=sessions/*}"
    };
  }

  // Report on the status of a session.
  // This generates a report detailing which tests have been completed,
  // and an overall rollup.
  rpc ReportSession(ReportSessionRequest) returns (ReportSessionResponse) {
    option (google.api.http) = {
      post: "/v1beta1/{name=sessions/*}:report"
    };
  }

  // Gets a testing session.
  rpc GetTest(GetTestRequest) returns (Test) {
    option (google.api.http) = {
      get: "/v1beta1/{name=tests/*}"
    };
    option (google.api.routing) = {
      routing_parameters {
        field: "name"
        path_template: "/v1beta1/{rename=tests/*}"
        }
      routing_parameters {
        field: "routing.name"
        path_template: "/v1beta1/{routing_id=tests/*}"
        }
    };
    option (google.api.method_signature) = "name";
  }

  // List the tests of a sessesion.
  rpc ListTests(ListTestsRequest) returns (ListTestsResponse) {
    option (google.api.http) = {
      get: "/v1beta1/{parent=sessions/*}/tests"
    };
  }

  // Explicitly decline to implement a test.
  //
  // This removes the test from subsequent `ListTests` calls, and
  // attempting to do the test will error.
  //
  // This method will error if attempting to delete a required test.
  rpc DeleteTest(DeleteTestRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1beta1/{name=sessions/*/tests/*}"
    };
  }

  // Register a response to a test.
  //
  // In cases where a test involves registering a final answer at the
  // end of the test, this method provides the means to do so.
  rpc VerifyTest(VerifyTestRequest) returns (VerifyTestResponse) {
    option (google.api.http) = {
      post: "/v1beta1/{name=sessions/*/tests/*}:check"
      body: "*"
      additional_bindings {
        post: "/v1beta1/{foo=sessions/*/tests/*}:check"
        body: "*"
      }
      additional_bindings {
        post: "/v1beta1/{answer=sessions/*/tests/*}:check"
        body: "*"
      }
      additional_bindings {
        post: "/v1beta1/{test_to_verify.name=sessions/*/tests/*}:check"
        body: "*"
      }
      // Test enums in HTTP fields.
      additional_bindings { post: "/v1beta1/{type}:check" body: "*" }
    };
  }
}

// A session is a suite of tests, generally being made in the context
// of testing code generation.
//
// A session defines tests it may expect, based on which version of the
// code generation spec is in use.
message Session {
  option (google.api.resource) = {
    type: "showcase.googleapis.com/Session"
    pattern: "sessions/{session}"
  };

  // The name of the session. The ID must conform to ^[a-z]+$
  // If this is not provided, Showcase chooses one at random.
  string name = 1;

  // The specification versions understood by Showcase.
  enum Version {
    // Unspecified version. If passed on creation, the session will default
    // to using the latest stable release.
    VERSION_UNSPECIFIED = 0;

    // The latest v1. Currently, this is v1.0.
    V1_LATEST = 1;

    // v1.0. (Until the spec is "GA", this will be a moving target.)
    V1_0 = 2;
  }

  // Required. The version this session is using.
  Version version = 2;

  map<int32, string> session_ids_to_descriptor = 3;
}

// The request for the CreateSession method.
message CreateSessionRequest {
  // The session to be created.
  // Sessions are immutable once they are created (although they can
  // be deleted).
  Session session = 1;

  // A blueprint is an explicit definition of methods and requests that are
  // needed to be made to test this specific test case. Ideally this would be
  // represented by something more robust like CEL, but as of writing this, I am
  // unsure if CEL is ready.
  message Blueprint {
    option (google.api.resource) = {
      type: "showcase.googleapis.com/Blueprint"
      pattern: "sessions/{session}/tests/{test}/blueprints/{blueprint}"
    };

    // The name of this blueprint.
    string name = 1;

    // A description of this blueprint.
    string description = 2;

    // A message representing a method invocation.
    message Invocation {
      // The fully qualified name of the showcase method to be invoked.
      string method = 1;

      // The request to be made if a specific request is necessary.
      bytes serialized_request = 2;
    }

    // The initial request to trigger this test.
    Invocation request = 3;
  }

  // The blueprints that will satisfy this test. There may be multiple
  // blueprints that can signal to the server that this test case is being
  // exercised. Although multiple blueprints are specified, only a single
  // blueprint needs to be run to signal that the test case was exercised.
  Blueprint blueprint = 2;
}

// The request for the GetSession method.
message GetSessionRequest {
  // The session to be retrieved.
  string name = 1 [(google.api.resource_reference).type =
                       "showcase.googleapis.com/Session"];
}

// The request for the ListSessions method.
message ListSessionsRequest {
  // The maximum number of sessions to return per page.
  int32 page_size = 1;

  // The page token, for retrieving subsequent pages.
  string page_token = 2;
}

// Response for the ListSessions method.
message ListSessionsResponse {
  // The sessions being returned.
  repeated Session sessions = 1;

  // The next page token, if any.
  // An empty value here means the last page has been reached.
  string next_page_token = 2;
}

// Request for the DeleteSession method.
message DeleteSessionRequest {
  // The session to be deleted.
  string name = 1 [(google.api.resource_reference).type =
                       "showcase.googleapis.com/Session"];
}

// Request message for reporting on a session.
message ReportSessionRequest {
  // The session to be reported on.
  string name = 1 [(google.api.resource_reference).type =
                       "showcase.googleapis.com/Session"];
}

// Response message for reporting on a session.
message ReportSessionResponse {
  // The topline state of the report.
  enum Result {
    RESULT_UNSPECIFIED = 0;

    // The session is complete, and everything passed.
    PASSED = 1;

    // The session had an explicit failure.
    FAILED = 2;

    // The session is incomplete. This is a failure response.
    INCOMPLETE = 3;
  }

  // The state of the report.
  Result result = 1;

  // The test runs of this session.
  repeated TestRun test_runs = 2;
}

message GetTestRequest {
  // The session to be retrieved.
  string name = 1
      [(google.api.resource_reference).type = "showcase.googleapis.com/Test"];
  Routing routing = 2;
}

message Routing {
  string name = 1;
}

message Test {
  option (google.api.resource) = {
    type: "showcase.googleapis.com/Test"
    pattern: "sessions/{session}/tests/{shard_id}~{test_id}"
  };

  // The name of the test.
  // The tests/* portion of the names are hard-coded, and do not change
  // from session to session.
  string name = 1;

  // Whether or not a test is required, recommended, or optional.
  enum ExpectationLevel {
    EXPECTATION_LEVEL_UNSPECIFIED = 0;

    // This test is strictly required.
    REQUIRED = 1;

    // This test is recommended.
    //
    // If a generator explicitly ignores a recommended test (see `DeleteTest`),
    // then the report may still pass, but with a warning.
    //
    // If a generator skips a recommended test and does not explicitly
    // express that intention, the report will fail.
    RECOMMENDED = 2;

    // This test is optional.
    //
    // If a generator explicitly ignores an optional test (see `DeleteTest`),
    // then the report may still pass, and no warning will be issued.
    //
    // If a generator skips an optional test and does not explicitly
    // express that intention, the report may still pass, but with a
    // warning.
    OPTIONAL = 3;
  }

  // The expectation level for this test.
  ExpectationLevel expectation_level = 2;

  // A description of the test.
  string description = 3;
}

// An issue found in the test.
message Issue {
  // The different potential types of issues.
  enum Type {
    TYPE_UNSPECIFIED = 0;

    // The test was never instrumented.
    SKIPPED = 1;

    // The test was started but never confirmed.
    PENDING = 2;

    // The test was instrumented, but Showcase got an unexpected
    // value when the generator tried to confirm success.
    INCORRECT_CONFIRMATION = 3;
  }

  // The type of the issue.
  Type type = 1;

  // Severity levels.
  enum Severity {
    SEVERITY_UNSPECIFIED = 0;

    // Errors.
    ERROR = 1;

    // Warnings.
    WARNING = 2;
  }

  // The severity of the issue.
  Severity severity = 2;

  // A description of the issue.
  string description = 3;
}

// The request for the ListTests method.
message ListTestsRequest {
  // The session.
  string parent = 1 [(google.api.resource_reference).type =
                         "showcase.googleapis.com/Session"];

  // The maximum number of tests to return per page.
  int32 page_size = 2;

  // The page token, for retrieving subsequent pages.
  string page_token = 3;
}

// The response for the ListTests method.
message ListTestsResponse {
  // The tests being returned.
  repeated Test tests = 1;

  // The next page token, if any.
  // An empty value here means the last page has been reached.
  string next_page_token = 2;
}

// A TestRun is the result of running a Test.
message TestRun {
  // The name of the test.
  // The tests/* portion of the names are hard-coded, and do not change
  // from session to session.
  string test = 1
      [(google.api.resource_reference).type = "showcase.googleapis.com/Test"];

  // An issue found with the test run. If empty, this test run was successful.
  Issue issue = 2;
}

// Request message for deleting a test.
message DeleteTestRequest {
  // The test to be deleted.
  string name = 1
      [(google.api.resource_reference).type = "showcase.googleapis.com/Test"];
}

message VerifyTestRequest {
  // The test to have an answer registered to it.
  string name = 1
      [(google.api.resource_reference).type = "showcase.googleapis.com/Test"];

  // The answer from the test.
  bytes answer = 2;

  // The answers from the test if multiple are to be checked
  repeated bytes answers = 3;

  // Test fields for HTTP annotations.
  string foo = 4;

  Test test_to_verify = 5;

  // For testing enums in HTTP fields.
  Issue.Type type = 6;
}

message VerifyTestResponse {
  // An issue if check answer was unsuccessful. This will be empty if the check
  // answer succeeded.
  Issue issue = 1;
}
