Blog changes + tests (#4)

* Blog changes + tests

* Fix build

* Fix

* Add back step

* Fix logger

* Fix test

* Typo

* Better test

* Changes to tests

* Update micro

* Fixing all them things

* Fixing even more things :))

* Bump micro

* Fix posts and tags by following micro changes

* Trying to pin workflow to correct micro version

* huh

* Bump go micro

* Add etcd replace

* Changing a bunch of things

* Denormalize to fix bug

* Fixes
This commit is contained in:
Janos Dobronszki
2020-10-15 15:09:59 +02:00
committed by GitHub
parent 4eb8479f9f
commit 6e8d7f4248
30 changed files with 1203 additions and 1449 deletions

11
tests/README.md Normal file
View File

@@ -0,0 +1,11 @@
# Tests Service
The tests service is for end to end testing
## Overview
This is a future service for end to end testing which either comes pre-built with integration tests in the
integration sub directory or with tests which are register via an endpoint Tests.Register for a callback
which then conforms to our testing API.

14
tests/handler/handler.go Normal file
View File

@@ -0,0 +1,14 @@
package handler
import (
"context"
pb "github.com/m3o/services/tests/proto"
)
type Tests struct{}
func (t *Tests) Register(ctx context.Context, req *pb.RegisterRequest, rsp *pb.RegisterResponse) error {
// TODO register the test to be run periodically
return nil
}

View File

@@ -0,0 +1,4 @@
**/Dockerfile
**/*.md
**/*.git
**/*.dockerignore

32
tests/image/Dockerfile Normal file
View File

@@ -0,0 +1,32 @@
FROM alpine:latest
RUN apk add make git go gcc libtool musl-dev curl bash
# Configure Go
ENV GOROOT /usr/lib/go
ENV GOPATH /go
ENV PATH /go/bin:$PATH
RUN mkdir -p ${GOPATH}/src ${GOPATH}/bin
RUN apk add ca-certificates && \
rm -rf /var/cache/apk/* /tmp/* && \
[ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf
RUN apk add --update ca-certificates openssl tar && \
wget https://github.com/coreos/etcd/releases/download/v3.4.7/etcd-v3.4.7-linux-amd64.tar.gz && \
tar xzvf etcd-v3.4.7-linux-amd64.tar.gz && \
mv etcd-v3.4.7-linux-amd64/etcd* /bin/ && \
apk del --purge tar openssl && \
rm -Rf etcd-v3.4.7-linux-amd64* /var/cache/apk/*
VOLUME /data
EXPOSE 2379 2380 4001 7001
ADD services/tests/image/run-etcd.sh /bin/run.sh
# Speeding up tests by predownloading and building dependencies for services used.
RUN mkdir services
COPY services/go.* services/
RUN cd services && go mod download
COPY services/ services
RUN bash -c 'for d in $(find services -name "main.go" | xargs -n 1 dirname); do pushd $d && go install && popd; done'
COPY ./micro/micro /microserver
ENTRYPOINT ["sh", "/bin/run.sh"]

8
tests/image/run-etcd.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
ETCD_CMD="/bin/etcd -data-dir=/data"
echo -e "Running '$ETCD_CMD'\nBEGIN ETCD OUTPUT\n"
exec $ETCD_CMD &
sleep 4
/microserver $*

5
tests/image/test-docker.sh Executable file
View File

@@ -0,0 +1,5 @@
cp services/tests/image/.dockerignore .
pushd micro
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build
popd
DOCKER_BUILDKIT=1 docker build -t micro -f services/tests/image/Dockerfile .

View File

@@ -0,0 +1,9 @@
# Integration Tests
This directory includes integration tests for m3o services
## Overview
This directory is for tests built into the tests service. In future there may be a integration.Register endpoint
and we just import the various tests from wherever they are but for they are standard go tests run when the
m3o flag is passed for integration testing

View File

@@ -0,0 +1,239 @@
// +build blog
package signup
import (
"encoding/json"
"errors"
"math/rand"
"os"
"os/exec"
"strings"
"testing"
"time"
"github.com/micro/micro/v3/test"
p "github.com/micro/services/blog/posts/handler"
)
const (
retryCount = 1
signupSuccessString = "Signup complete"
)
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz")
func randStringRunes(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
func setupBlogTests(serv test.Server, t *test.T) {
envToConfigKey := map[string][]string{}
if err := test.Try("Set up config values", t, func() ([]byte, error) {
for envKey, configKeys := range envToConfigKey {
val := os.Getenv(envKey)
if len(val) == 0 {
t.Fatalf("'%v' flag is missing", envKey)
}
for _, configKey := range configKeys {
outp, err := serv.Command().Exec("config", "set", configKey, val)
if err != nil {
return outp, err
}
}
}
return serv.Command().Exec("config", "set", "micro.billing.max_included_services", "3")
}, 10*time.Second); err != nil {
t.Fatal(err)
return
}
services := []struct {
envVar string
deflt string
}{
{envVar: "POSTS_SVC", deflt: "../../../blog/posts"},
{envVar: "TAGS_SVC", deflt: "../../../blog/tags"},
}
for _, v := range services {
outp, err := serv.Command().Exec("run", v.deflt)
if err != nil {
t.Fatal(string(outp))
return
}
}
if err := test.Try("Find posts and tags", t, func() ([]byte, error) {
outp, err := serv.Command().Exec("services")
if err != nil {
return outp, err
}
list := []string{"posts", "tags"}
logOutp := []byte{}
fail := false
for _, s := range list {
if !strings.Contains(string(outp), s) {
o, _ := serv.Command().Exec("logs", s)
logOutp = append(logOutp, o...)
fail = true
}
}
if fail {
return append(outp, logOutp...), errors.New("Can't find required services in list")
}
return outp, err
}, 180*time.Second); err != nil {
return
}
// setup rules
// Adjust rules before we signup into a non admin account
outp, err := serv.Command().Exec("auth", "create", "rule", "--access=granted", "--scope=''", "--resource=\"service:posts:*\"", "posts")
if err != nil {
t.Fatalf("Error setting up rules: %v", outp)
return
}
// copy the config with the admin logged in so we can use it for reading logs
// we dont want to have an open access rule for logs as it's not how it works in live
confPath := serv.Command().Config
outp, err = exec.Command("cp", "-rf", confPath, confPath+".admin").CombinedOutput()
if err != nil {
t.Fatalf("Error copying config: %v", outp)
return
}
}
func TestPostsService(t *testing.T) {
test.TrySuite(t, testPosts, retryCount)
}
// count is a string in responses...
type protoTag struct {
Title string `json:"title"`
Slug string `json:"slug"`
Type string `json:"type"`
Count string `json:"count"`
}
func testPosts(t *test.T) {
t.Parallel()
serv := test.NewServer(t, test.WithLogin())
defer serv.Close()
if err := serv.Run(); err != nil {
return
}
setupBlogTests(serv, t)
cmd := serv.Command()
if err := test.Try("Save post", t, func() ([]byte, error) {
// Attention! The content must be unquoted, don't add quotes.
outp, err := cmd.Exec("posts", "--id=1", "--title=Hi", "--content=Hi there", "--tags=a,b", "save")
if err != nil {
outp1, _ := cmd.Exec("logs", "posts")
return append(outp, outp1...), err
}
return outp, err
}, 15*time.Second); err != nil {
return
}
outp, err := cmd.Exec("posts", "query")
if err != nil {
t.Fatal(string(outp))
}
expected := []p.Post{
{
ID: "1",
Title: "Hi",
Content: "Hi there",
Tags: []string{"a", "b"},
},
}
type rsp struct {
Posts []p.Post `json:"posts"`
}
var actual rsp
json.Unmarshal(outp, &actual)
if len(actual.Posts) == 0 {
t.Fatal(string(outp))
return
}
if expected[0].ID != actual.Posts[0].ID ||
expected[0].Title != actual.Posts[0].Title ||
expected[0].Content != actual.Posts[0].Content ||
len(expected[0].Tags) != len(actual.Posts[0].Tags) {
t.Fatal(expected[0], actual.Posts[0])
}
outp, err = cmd.Exec("tags", "list", "--type=post-tag")
type tagsRsp struct {
Tags []protoTag `json:"tags"`
}
var tagsActual tagsRsp
json.Unmarshal(outp, &tagsActual)
if len(tagsActual.Tags) == 0 {
outp1, _ := cmd.Exec("logs", "tags")
t.Fatal(string(append(outp, outp1...)))
return
}
if len(tagsActual.Tags) != 2 {
t.Fatal(tagsActual.Tags)
return
}
if tagsActual.Tags[0].Count != "1" {
t.Fatal(tagsActual.Tags[0])
return
}
if tagsActual.Tags[1].Count != "1" {
t.Fatal(tagsActual.Tags[1])
return
}
time.Sleep(5 * time.Second)
// Inserting an other post so tag counts increase
outp, err = cmd.Exec("posts", "--id=2", "--title=Hi1", "--content=Hi there1", "--tags=a,b", "save")
if err != nil {
t.Fatal(string(outp))
return
}
outp, err = cmd.Exec("tags", "list", "--type=post-tag")
json.Unmarshal(outp, &tagsActual)
if len(tagsActual.Tags) == 0 {
outp1, _ := cmd.Exec("logs", "tags")
t.Fatal(string(append(outp, outp1...)))
return
}
if len(tagsActual.Tags) != 2 {
t.Fatal(tagsActual.Tags)
return
}
if tagsActual.Tags[0].Count != "2" {
outp1, _ := cmd.Exec("store", "list", "--table=tags")
outp2, _ := cmd.Exec("store", "list", "--table=posts")
t.Fatal(tagsActual.Tags[0], string(outp1), string(outp2))
return
}
if tagsActual.Tags[1].Count != "2" {
outp1, _ := cmd.Exec("store", "list", "--table=tags")
outp2, _ := cmd.Exec("store", "list", "--table=posts")
t.Fatal(tagsActual.Tags[1], string(outp1), string(outp2))
return
}
}

19
tests/main.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import (
"github.com/m3o/services/tests/handler"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/micro/v3/service"
)
func main() {
service := service.New(
service.Name("tests"),
)
service.Handle(new(handler.Tests))
if err := service.Run(); err != nil {
logger.Fatal(err)
}
}

184
tests/proto/tests.pb.go Normal file
View File

@@ -0,0 +1,184 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: tests/proto/tests.proto
package go_micro_service_tests
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Test struct {
// name of the test
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// service to call back
Service string `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"`
// endpoint to call back
Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
// how often to run the test in seconds
Interval int64 `protobuf:"varint,4,opt,name=interval,proto3" json:"interval,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Test) Reset() { *m = Test{} }
func (m *Test) String() string { return proto.CompactTextString(m) }
func (*Test) ProtoMessage() {}
func (*Test) Descriptor() ([]byte, []int) {
return fileDescriptor_8daabec0b76982a6, []int{0}
}
func (m *Test) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Test.Unmarshal(m, b)
}
func (m *Test) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Test.Marshal(b, m, deterministic)
}
func (m *Test) XXX_Merge(src proto.Message) {
xxx_messageInfo_Test.Merge(m, src)
}
func (m *Test) XXX_Size() int {
return xxx_messageInfo_Test.Size(m)
}
func (m *Test) XXX_DiscardUnknown() {
xxx_messageInfo_Test.DiscardUnknown(m)
}
var xxx_messageInfo_Test proto.InternalMessageInfo
func (m *Test) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Test) GetService() string {
if m != nil {
return m.Service
}
return ""
}
func (m *Test) GetEndpoint() string {
if m != nil {
return m.Endpoint
}
return ""
}
func (m *Test) GetInterval() int64 {
if m != nil {
return m.Interval
}
return 0
}
type RegisterRequest struct {
Tests []*Test `protobuf:"bytes,1,rep,name=tests,proto3" json:"tests,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RegisterRequest) Reset() { *m = RegisterRequest{} }
func (m *RegisterRequest) String() string { return proto.CompactTextString(m) }
func (*RegisterRequest) ProtoMessage() {}
func (*RegisterRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_8daabec0b76982a6, []int{1}
}
func (m *RegisterRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RegisterRequest.Unmarshal(m, b)
}
func (m *RegisterRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RegisterRequest.Marshal(b, m, deterministic)
}
func (m *RegisterRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RegisterRequest.Merge(m, src)
}
func (m *RegisterRequest) XXX_Size() int {
return xxx_messageInfo_RegisterRequest.Size(m)
}
func (m *RegisterRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RegisterRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RegisterRequest proto.InternalMessageInfo
func (m *RegisterRequest) GetTests() []*Test {
if m != nil {
return m.Tests
}
return nil
}
type RegisterResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RegisterResponse) Reset() { *m = RegisterResponse{} }
func (m *RegisterResponse) String() string { return proto.CompactTextString(m) }
func (*RegisterResponse) ProtoMessage() {}
func (*RegisterResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8daabec0b76982a6, []int{2}
}
func (m *RegisterResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RegisterResponse.Unmarshal(m, b)
}
func (m *RegisterResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RegisterResponse.Marshal(b, m, deterministic)
}
func (m *RegisterResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RegisterResponse.Merge(m, src)
}
func (m *RegisterResponse) XXX_Size() int {
return xxx_messageInfo_RegisterResponse.Size(m)
}
func (m *RegisterResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RegisterResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RegisterResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*Test)(nil), "go.micro.service.tests.Test")
proto.RegisterType((*RegisterRequest)(nil), "go.micro.service.tests.RegisterRequest")
proto.RegisterType((*RegisterResponse)(nil), "go.micro.service.tests.RegisterResponse")
}
func init() { proto.RegisterFile("tests/proto/tests.proto", fileDescriptor_8daabec0b76982a6) }
var fileDescriptor_8daabec0b76982a6 = []byte{
// 217 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0x41, 0x4b, 0x03, 0x31,
0x10, 0x85, 0x8d, 0xbb, 0xd5, 0x3a, 0x1e, 0x94, 0x39, 0x68, 0x28, 0x1e, 0x96, 0x5c, 0xcc, 0x29,
0x85, 0xf5, 0x37, 0xf8, 0x07, 0x82, 0x77, 0xa9, 0x75, 0xa8, 0x81, 0x36, 0x59, 0x33, 0xb1, 0xbf,
0x5f, 0x32, 0xeb, 0xae, 0x20, 0x8a, 0xb7, 0xf7, 0xf2, 0x1e, 0xf3, 0x3e, 0x02, 0xb7, 0x85, 0xb8,
0xf0, 0x7a, 0xc8, 0xa9, 0xa4, 0xb5, 0x68, 0x27, 0x1a, 0x6f, 0x76, 0xc9, 0x1d, 0xc2, 0x36, 0x27,
0xc7, 0x94, 0x8f, 0x61, 0x4b, 0x4e, 0x52, 0xb3, 0x87, 0xf6, 0x89, 0xb8, 0x20, 0x42, 0x1b, 0x37,
0x07, 0xd2, 0xaa, 0x53, 0xf6, 0xc2, 0x8b, 0x46, 0x0d, 0xe7, 0x5f, 0x65, 0x7d, 0x2a, 0xcf, 0x93,
0xc5, 0x15, 0x2c, 0x29, 0xbe, 0x0e, 0x29, 0xc4, 0xa2, 0x1b, 0x89, 0x66, 0x5f, 0xb3, 0x10, 0x0b,
0xe5, 0xe3, 0x66, 0xaf, 0xdb, 0x4e, 0xd9, 0xc6, 0xcf, 0xde, 0x3c, 0xc2, 0x95, 0xa7, 0x5d, 0xe0,
0x42, 0xd9, 0xd3, 0xfb, 0x47, 0x1d, 0xee, 0x61, 0x21, 0x24, 0x5a, 0x75, 0x8d, 0xbd, 0xec, 0xef,
0xdc, 0xef, 0xa0, 0xae, 0x52, 0xfa, 0xb1, 0x6a, 0x10, 0xae, 0xbf, 0xcf, 0xf0, 0x90, 0x22, 0x53,
0xff, 0x06, 0x8b, 0x5a, 0x61, 0x7c, 0x86, 0xe5, 0x14, 0xe2, 0xfd, 0x5f, 0xd7, 0x7e, 0x50, 0xac,
0xec, 0xff, 0xc5, 0x71, 0xc7, 0x9c, 0xbc, 0x9c, 0xc9, 0x8f, 0x3e, 0x7c, 0x06, 0x00, 0x00, 0xff,
0xff, 0x2d, 0xa4, 0xe4, 0x69, 0x6c, 0x01, 0x00, 0x00,
}

View File

@@ -0,0 +1,95 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: tests/proto/tests.proto
package go_micro_service_tests
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
api "github.com/micro/go-micro/v3/api"
client "github.com/micro/go-micro/v3/client"
server "github.com/micro/go-micro/v3/server"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Tests service
func NewTestsEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Tests service
type TestsService interface {
// enables registering an endpoint for callback to run tests
Register(ctx context.Context, in *RegisterRequest, opts ...client.CallOption) (*RegisterResponse, error)
}
type testsService struct {
c client.Client
name string
}
func NewTestsService(name string, c client.Client) TestsService {
return &testsService{
c: c,
name: name,
}
}
func (c *testsService) Register(ctx context.Context, in *RegisterRequest, opts ...client.CallOption) (*RegisterResponse, error) {
req := c.c.NewRequest(c.name, "Tests.Register", in)
out := new(RegisterResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Tests service
type TestsHandler interface {
// enables registering an endpoint for callback to run tests
Register(context.Context, *RegisterRequest, *RegisterResponse) error
}
func RegisterTestsHandler(s server.Server, hdlr TestsHandler, opts ...server.HandlerOption) error {
type tests interface {
Register(ctx context.Context, in *RegisterRequest, out *RegisterResponse) error
}
type Tests struct {
tests
}
h := &testsHandler{hdlr}
return s.Handle(s.NewHandler(&Tests{h}, opts...))
}
type testsHandler struct {
TestsHandler
}
func (h *testsHandler) Register(ctx context.Context, in *RegisterRequest, out *RegisterResponse) error {
return h.TestsHandler.Register(ctx, in, out)
}

26
tests/proto/tests.proto Normal file
View File

@@ -0,0 +1,26 @@
syntax = "proto3";
package go.micro.service.tests;
service Tests {
// enables registering an endpoint for callback to run tests
rpc Register(RegisterRequest) returns (RegisterResponse) {};
}
message Test {
// name of the test
string name = 1;
// service to call back
string service = 2;
// endpoint to call back
string endpoint = 3;
// how often to run the test in seconds
int64 interval = 4;
// TODO: data to include in the callback
}
message RegisterRequest {
repeated Test tests = 1;
}
message RegisterResponse {}