1// Copyright 2011 Google Inc. All rights reserved.
2// Use of this source code is governed by the Apache 2.0
3// license that can be found in the LICENSE file.
4
5// Package aetesting provides utilities for testing App Engine packages.
6// This is not for testing user applications.
7package aetesting
8
9import (
10	"fmt"
11	"net/http"
12	"reflect"
13	"testing"
14
15	"github.com/golang/protobuf/proto"
16	"golang.org/x/net/context"
17
18	"google.golang.org/appengine/internal"
19)
20
21// FakeSingleContext returns a context whose Call invocations will be serviced
22// by f, which should be a function that has two arguments of the input and output
23// protocol buffer type, and one error return.
24func FakeSingleContext(t *testing.T, service, method string, f interface{}) context.Context {
25	fv := reflect.ValueOf(f)
26	if fv.Kind() != reflect.Func {
27		t.Fatal("not a function")
28	}
29	ft := fv.Type()
30	if ft.NumIn() != 2 || ft.NumOut() != 1 {
31		t.Fatalf("f has %d in and %d out, want 2 in and 1 out", ft.NumIn(), ft.NumOut())
32	}
33	for i := 0; i < 2; i++ {
34		at := ft.In(i)
35		if !at.Implements(protoMessageType) {
36			t.Fatalf("arg %d does not implement proto.Message", i)
37		}
38	}
39	if ft.Out(0) != errorType {
40		t.Fatalf("f's return is %v, want error", ft.Out(0))
41	}
42	s := &single{
43		t:       t,
44		service: service,
45		method:  method,
46		f:       fv,
47	}
48	return internal.WithCallOverride(internal.ContextForTesting(&http.Request{}), s.call)
49}
50
51var (
52	protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
53	errorType        = reflect.TypeOf((*error)(nil)).Elem()
54)
55
56type single struct {
57	t               *testing.T
58	service, method string
59	f               reflect.Value
60}
61
62func (s *single) call(ctx context.Context, service, method string, in, out proto.Message) error {
63	if service == "__go__" {
64		if method == "GetNamespace" {
65			return nil // always yield an empty namespace
66		}
67		return fmt.Errorf("Unknown API call /%s.%s", service, method)
68	}
69	if service != s.service || method != s.method {
70		s.t.Fatalf("Unexpected call to /%s.%s", service, method)
71	}
72	ins := []reflect.Value{
73		reflect.ValueOf(in),
74		reflect.ValueOf(out),
75	}
76	outs := s.f.Call(ins)
77	if outs[0].IsNil() {
78		return nil
79	}
80	return outs[0].Interface().(error)
81}
82