├── LICENSE
├── README.md
├── build.sh
├── config
└── config.go
├── index.js
├── lib
├── nodewrapper
│ ├── nodewrapper.go
│ └── nodewrapper_test.go
└── osutil
│ ├── osutil.go
│ └── osutil_test.go
├── main.go
└── main_test.go
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Patrick Kosterman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AWS Lambda Barcode-generator
2 | ===========
3 |
4 | This is a sample project to demonstrate usage of Golang for AWS Lambda.
5 |
6 | AWS Lambda is a cloud computing service that lets you run code without provisioning or managing servers. AWS Lambda executes your code only when needed and scales automatically.
7 |
8 | Currently AWS Lambda natively supports Java, Node.js, Python, and C#.
9 |
10 | This project uses a Node.js wrapper to build a Go Lambda function that generates and returns a barcode when triggered by AWS API Gateway. The barcode is returned as a base64 encoded PNG image string.
11 |
12 | The Node.js wrapper keeps a Go process around to handle multiple invocations. The first time the function runs it will take a bit longer, but after that it greatly increases performance.
13 |
14 | ## Why Go?
15 |
16 | I have been using Go for the past 3 years, it's our language of choice at PassKit because of:
17 |
18 | * Speed! Very fast & a perfect choice for CPU-intensive tasks.
19 | * Quick & easy to master in a very short amount of time.
20 | * Portability across platforms.
21 | * Compiled binariers: plays nice with Docker.
22 | * Excellent concurreny primitives.
23 | * Well defined error handling patterns.
24 | * Rich standard libraries.
25 | * Standard code formatting / ease of maintenance.
26 |
27 | ## Inspiration
28 | The Node.js wrapper used in this project is inspired by:
29 | * lambda_proc
30 | * Amazon AWS Lambda & Tcl
31 |
32 | ## Full Demo & Instructions
33 |
34 | Click here for a detailed article on how to set this up with AWS API Gateway and Route 53.
35 |
36 | ## Build
37 | Clone this repo and cd into the project root then:
38 |
39 | ```bash
40 | ./build.sh
41 | ```
42 |
43 | This will place a lambda.zip file into the build folder. You can update this zip file
44 | into AWS Lambda.
45 |
46 | If you want to run `go test`, then you will also need to install lambda-test (Node command-line tool). I will
47 | place a repo of this on github shortly - just need to rewrite some of the logic.
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | # clean the build directory
6 | echo "Cleaning build folder"
7 | test -d "./build" && rm -rf build
8 | mkdir ./build
9 |
10 | echo "Setting Production Mode"
11 | mv config/config.go config/config.tmp
12 | sed -e "/const Develop = /s/.*/const Develop = false/g" config/config.tmp > config/config.go
13 |
14 | echo "Compiling for Linux"
15 | GOOS=linux go build -o ./build/main
16 | cp index.js build
17 |
18 | echo "Preparing Lambda bundle"
19 | cd build
20 | sed -e "/const exeName = /s/.*/const exeName = 'main';/g" index.js > index.tmp
21 | sed -e "s/done(output, null);/done(output.errorMessage, null);/g" index.tmp > index.js
22 | zip -r lambda.zip main index.js
23 |
24 | echo "Cleaning up"
25 | rm index.* main
26 | mv ../config/config.tmp ../config/config.go
27 | echo "Done!"
28 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | // NOTE Build Script will change develop flag to false
4 | const DEVELOP = true
5 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Will pass a JSON string via stdin to the go function and will catch and relay
2 | // back the JSON response via stdout. The go executable process will be reused
3 | // and restarted upon faileure. This should provide lower latency after the
4 | // first call.
5 |
6 | // set to name of the package for local testing - buildscript will rename to 'main'
7 | const exeName = 'aws-lambda-barcode-generator';
8 |
9 | // It shouldn't be necessary to modify below this line
10 | const MAX_FAILS = 4;
11 |
12 | var child_process = require('child_process'),
13 | go_proc = null,
14 | done = console.log.bind(console),
15 | fails = 0;
16 |
17 | (function new_go_proc() {
18 |
19 | // pipe stdin/out, blind passthru stderr
20 | go_proc = child_process.spawn('./' + exeName, { stdio: ['pipe', 'pipe', process.stderr] });
21 |
22 | go_proc.on('error', function(err) {
23 | process.stderr.write("go_proc errored: "+JSON.stringify(err)+"\n");
24 | if (++fails > MAX_FAILS) {
25 | process.exit(1); // force container restart after too many fails
26 | }
27 | new_go_proc();
28 | done(err);
29 | });
30 |
31 | go_proc.on('exit', function(code) {
32 | process.stderr.write("go_proc exited prematurely with code: "+code+"\n");
33 | if (++fails > MAX_FAILS) {
34 | process.exit(1); // force container restart after too many fails
35 | }
36 | new_go_proc();
37 | done(new Error("Exited with code "+code));
38 | });
39 |
40 | go_proc.stdin.on('error', function(err) {
41 | process.stderr.write("go_proc stdin write error: "+JSON.stringify(err)+"\n");
42 | if (++fails > MAX_FAILS) {
43 | process.exit(1); // force container restart after too many fails
44 | }
45 | new_go_proc();
46 | done(err);
47 | });
48 |
49 | var data = null;
50 | go_proc.stdout.on('data', function(chunk) {
51 | fails = 0; // reset fails
52 | if (data === null) {
53 | data = new Buffer(chunk);
54 | } else {
55 | data.write(chunk);
56 | }
57 | // check for newline ascii char 10
58 | if (data.length && data[data.length-1] == 10) {
59 | var output = JSON.parse(data.toString('UTF-8'));
60 | data = null;
61 | if (output.errorMessage) {
62 | // line will be replaced with 'done(output.errorMessage, null)' by build script
63 | done(output, null);
64 | } else {
65 | done(null, output);
66 | }
67 | };
68 | });
69 | })();
70 |
71 | exports.handler = function(event, context) {
72 |
73 | // always output to current context's done
74 | done = context.done.bind(context);
75 |
76 | go_proc.stdin.write(JSON.stringify({
77 | "event": event,
78 | "context": context
79 | })+"\n");
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/lib/nodewrapper/nodewrapper.go:
--------------------------------------------------------------------------------
1 | package nodewrapper
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "log"
8 | "os"
9 | "strings"
10 | )
11 |
12 | type (
13 | Handler func(*Context, json.RawMessage) (interface{}, error)
14 |
15 | Context struct {
16 | AwsRequestID string `json:"awsRequestId"`
17 | FunctionName string `json:"functionName"`
18 | FunctionVersion string `json:"functionVersion"`
19 | Invokeid string `json:"invokeid"`
20 | IsDefaultFunctionVersion bool `json:"isDefaultFunctionVersion"`
21 | LogGroupName string `json:"logGroupName"`
22 | LogStreamName string `json:"logStreamName"`
23 | MemoryLimitInMB string `json:"memoryLimitInMB"`
24 | }
25 |
26 | Payload struct {
27 | // custom event fields
28 | Event json.RawMessage `json:"event"`
29 |
30 | // default context object
31 | Context *Context `json:"context"`
32 | }
33 |
34 | Response struct {
35 | // Request id is an incremental integer
36 | // representing the request that has been
37 | // received by this go proc during it's
38 | // lifetime
39 | Success *bool `json:"success,omitempty"`
40 | RequestId int `json:"-"` // can retrun request_id for debugging"`
41 | // Any errors that occur during processing
42 | // or are returned by handlers are returned
43 | Error *string `json:"errorMessage,omitempty"`
44 | Token *string `json:"token,omitempty"`
45 | // Response output data returned if no errors are present
46 | Data *interface{} `json:"response,omitempty"`
47 | }
48 | )
49 |
50 | var requestId int // process req id
51 |
52 | func NewErrorResponse(err error) *Response {
53 | e := err.Error()
54 | errorEscaped := strings.Replace(strings.Replace(e, "\n", "", -1), "\t", " ", -1)
55 | return &Response{
56 | RequestId: requestId,
57 | Error: &errorEscaped,
58 | }
59 | }
60 |
61 | func NewResponse(data interface{}) *Response {
62 | success := true
63 |
64 | if token, ok := data.(map[string]interface{})["token"]; ok {
65 |
66 | delete(data.(map[string]interface{}), "token")
67 |
68 | return &Response{
69 | Success: &success,
70 | Token: token.(*string),
71 | RequestId: requestId,
72 | Data: &data,
73 | }
74 | }
75 |
76 | return &Response{
77 | Success: &success,
78 | RequestId: requestId,
79 | Data: &data,
80 | }
81 | }
82 |
83 | func Run(handler Handler, rawOutput bool) {
84 | RunStream(handler, os.Stdin, os.Stdout, rawOutput)
85 | }
86 |
87 | func RunStream(handler Handler, Stdin io.Reader, Stdout io.Writer, rawOutput bool) {
88 |
89 | stdin := json.NewDecoder(Stdin)
90 | stdout := json.NewEncoder(Stdout)
91 | decodeError := false
92 |
93 | for ; ; requestId++ {
94 | if err := func() (err error) {
95 | defer func() {
96 | if e := recover(); e != nil {
97 | err = fmt.Errorf("Server Error: 'Panic' %v", e)
98 | }
99 | }()
100 | var payload Payload
101 | if err := stdin.Decode(&payload); err != nil {
102 | decodeError = true
103 | return fmt.Errorf("Bad Request: %v", err)
104 | }
105 | data, err := handler(payload.Context, payload.Event)
106 | if err != nil {
107 | return err
108 | }
109 |
110 | if rawOutput {
111 | // for this particular purpose return as bytestream
112 | return stdout.Encode(data)
113 | }
114 | return stdout.Encode(NewResponse(data))
115 | }(); err != nil {
116 | if encErr := stdout.Encode(NewErrorResponse(err)); encErr != nil {
117 | // bad times
118 | requestId++
119 | break
120 | log.Println("Failed to encode err response!", encErr.Error())
121 | } else {
122 | // if invalid JSON, break to advance stdin and restart the loop
123 | if decodeError {
124 | requestId++
125 | break
126 | }
127 | }
128 | }
129 | }
130 |
131 | RunStream(handler, Stdin, Stdout, rawOutput)
132 | }
133 |
--------------------------------------------------------------------------------
/lib/nodewrapper/nodewrapper_test.go:
--------------------------------------------------------------------------------
1 | package nodewrapper
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "io"
7 | "testing"
8 | )
9 |
10 | func TestRunStream(t *testing.T) {
11 |
12 | const TestRecords = 100
13 |
14 | type Record struct {
15 | Id int `json:"id"`
16 | }
17 |
18 | ctx := &Context{
19 | AwsRequestID: "awsRequestId",
20 | FunctionName: "functionName",
21 | FunctionVersion: "functionVersion",
22 | Invokeid: "invokeid",
23 | IsDefaultFunctionVersion: true,
24 | LogGroupName: "logGroupName",
25 | LogStreamName: "logStreamName",
26 | MemoryLimitInMB: "memoryLimitInMB",
27 | }
28 |
29 | records := &bytes.Buffer{}
30 | enc := json.NewEncoder(records)
31 | for i := 0; i < TestRecords; i++ {
32 | data, err := json.Marshal(&Record{Id: i})
33 | if err != nil {
34 | t.Error(err)
35 | }
36 | if err := enc.Encode(&Payload{Context: ctx, Event: json.RawMessage(data)}); err != nil {
37 | t.Error(err)
38 | }
39 | }
40 |
41 | r, w := io.Pipe()
42 |
43 | go func() {
44 | RunStream(func(c *Context, data json.RawMessage) (interface{}, error) {
45 | if c.AwsRequestID != ctx.AwsRequestID {
46 | t.Errorf("Expected %v, got %v", ctx.AwsRequestID, c.AwsRequestID)
47 | }
48 | if c.FunctionName != ctx.FunctionName {
49 | t.Errorf("Expected %v, got %v", ctx.FunctionName, c.FunctionName)
50 | }
51 | if c.FunctionVersion != ctx.FunctionVersion {
52 | t.Errorf("Expected %v, got %v", ctx.FunctionVersion, c.FunctionVersion)
53 | }
54 | if c.Invokeid != ctx.Invokeid {
55 | t.Errorf("Expected %v, got %v", ctx.Invokeid, c.Invokeid)
56 | }
57 | if c.IsDefaultFunctionVersion != ctx.IsDefaultFunctionVersion {
58 | t.Errorf("Expected %v, got %v", ctx.IsDefaultFunctionVersion, c.IsDefaultFunctionVersion)
59 | }
60 | if c.LogGroupName != ctx.LogGroupName {
61 | t.Errorf("Expected %v, got %v", ctx.LogGroupName, c.LogGroupName)
62 | }
63 | if c.LogStreamName != ctx.LogStreamName {
64 | t.Errorf("Expected %v, got %v", ctx.LogStreamName, c.LogStreamName)
65 | }
66 | if c.MemoryLimitInMB != ctx.MemoryLimitInMB {
67 | t.Errorf("Expected %v, got %v", ctx.MemoryLimitInMB, c.MemoryLimitInMB)
68 | }
69 | var rec Record
70 | if err := json.Unmarshal(data, &rec); err != nil {
71 | t.Error(err)
72 | }
73 | return &rec, nil
74 | }, records, w)
75 | }()
76 |
77 | dec := json.NewDecoder(r)
78 | for i := 0; i < TestRecords; i++ {
79 | var resp Response
80 | if err := dec.Decode(&resp); err != nil {
81 | t.Error(err)
82 | }
83 | if resp.Error != nil {
84 | t.Errorf("Expected nil error, got: %v", resp.Error)
85 | }
86 |
87 | // Removed for production
88 | /*if resp.RequestId != i {
89 | t.Errorf("Expected %d, got %d", i, resp.RequestId)
90 | }*/
91 |
92 | testData := *resp.Data
93 | data, ok := testData.(map[string]interface{})
94 | if !ok {
95 | t.Errorf("Expected type map[string]interface{}, got %T", testData)
96 | }
97 | if data["id"].(float64) != float64(i) {
98 | t.Errorf("Expected %d, got %v", i, data["id"])
99 | }
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/lib/osutil/osutil.go:
--------------------------------------------------------------------------------
1 | package osutil
2 |
3 | import (
4 | "bytes"
5 | "os/exec"
6 | "strings"
7 | )
8 |
9 | // Helper function to make simplify command line access
10 | func Run(dir string, commandLine string) ([]byte, error) {
11 |
12 | // Split commandLine into an array separated by whitespace
13 | args := strings.Fields(commandLine)
14 | cmd := exec.Command(args[0], args[1:]...)
15 | cmd.Dir = dir
16 | var buf bytes.Buffer
17 | cmd.Stdout = &buf
18 | cmd.Stderr = &buf
19 | err := cmd.Run()
20 | out := buf.Bytes()
21 | if err != nil {
22 | return out, err
23 | }
24 | return out, nil
25 | }
26 |
--------------------------------------------------------------------------------
/lib/osutil/osutil_test.go:
--------------------------------------------------------------------------------
1 | package osutil
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestRunExec(t *testing.T) {
10 |
11 | assert := assert.New(t)
12 |
13 | ls, err := Run("./", "ls -la")
14 | assert.Nil(err, "Run returned an unexpected error")
15 | if assert.NotNil(ls, "Run ls -la failed to return a result") {
16 | assert.Contains(string(ls), "osutil.go", "Run 'ls -la' failed to contain file in chosen folder")
17 | assert.Contains(string(ls), "osutil_test.go", "Run 'ls -la' failed to contain second file in chosen folder")
18 | }
19 |
20 | // Test with many paramaters
21 | ls, err = Run("../../../", `stat -f %N-%A main.go`)
22 | assert.Nil(err, "Run returned an unexpected error")
23 | if assert.NotNil(ls, "Run ls -la failed to return a result") {
24 | assert.Contains(string(ls), "main.go-644", "Run 'stat -f' failed to contain file in chosen folder")
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "encoding/json"
7 | "fmt"
8 | "image/png"
9 | "os"
10 |
11 | "aws-lambda-barcode-generator/config"
12 | "aws-lambda-barcode-generator/lib/nodewrapper"
13 |
14 | "github.com/boombuler/barcode"
15 | "github.com/boombuler/barcode/pdf417"
16 | "github.com/boombuler/barcode/qr"
17 | )
18 |
19 | type LambdaInput struct {
20 | Width *int `json:"width,omitempty"`
21 | Height *int `json:"height,omitempty"`
22 | Message *string `json:"message,omitempty"`
23 | Type *string `json:"type,omitempty"`
24 | }
25 |
26 | func main() {
27 | nodewrapper.Run(func(context *nodewrapper.Context, eventJSON json.RawMessage) (interface{}, error) {
28 |
29 | var input LambdaInput
30 | var c barcode.Barcode
31 | var err error
32 |
33 | if err = json.Unmarshal(eventJSON, &input); err != nil {
34 | // Add error prefixes to all final error strings to allow for AWS API Gateway regex
35 | return nil, fmt.Errorf("400 Bad Request: Invalid Request - cannot marshal JSON input. Object received: %s", string(eventJSON))
36 | }
37 |
38 | err = checkValidRequest(input)
39 |
40 | if err != nil {
41 | return nil, err
42 | }
43 |
44 | switch barcodeType := *input.Type; barcodeType {
45 | case "qr":
46 | c, err = qr.Encode(*input.Message, qr.M, qr.Unicode)
47 | case "pdf417":
48 | c, err = pdf417.Encode(*input.Message, 3)
49 | }
50 |
51 | if err != nil {
52 | return nil, fmt.Errorf("500 Server Error: Could not generate barcode. Details: %s", err)
53 | }
54 |
55 | // Scale the barcode to 200x200 pixels
56 | c, err = barcode.Scale(c, *input.Width, *input.Height)
57 |
58 | if err != nil {
59 | return nil, fmt.Errorf("500 Server Error: Could not scale the barcode to input width and height. Details: %s", err)
60 | }
61 |
62 | // if we have develop set to true, then also export as PNG (so can view on local machine)
63 | if config.DEVELOP {
64 | // create the output file
65 | file, _ := os.Create("code.png")
66 | defer file.Close()
67 |
68 | // encode the barcode as png
69 | png.Encode(file, c)
70 | }
71 |
72 | // write image to buffer
73 | buf := new(bytes.Buffer)
74 | err = png.Encode(buf, c)
75 |
76 | if err != nil {
77 | return nil, fmt.Errorf("500 Server Error: Could not write image to buffer. Details: %s", err)
78 | }
79 |
80 | // return the image as a base64 encoded string
81 | return base64.StdEncoding.EncodeToString(buf.Bytes()), err
82 | }, true)
83 | }
84 |
85 | func checkValidRequest(input LambdaInput) error {
86 | // check if input conforms with our spec
87 | if input.Height == nil {
88 | return fmt.Errorf("400 Bad Request: 'height' field is missing.")
89 | }
90 |
91 | if input.Width == nil {
92 | return fmt.Errorf("400 Bad Request: 'width' field is missing.")
93 | }
94 |
95 | if input.Message == nil {
96 | return fmt.Errorf("400 Bad Request: 'message' field is missing.")
97 | }
98 |
99 | if input.Type == nil {
100 | return fmt.Errorf("400 Bad Request: 'type' field is missing.")
101 | }
102 |
103 | h := *input.Height
104 | w := *input.Width
105 | m := *input.Message
106 | t := *input.Type
107 |
108 | if h < 150 {
109 | return fmt.Errorf("400 Bad Request: 'height' needs to be a minimum of 150.")
110 | }
111 |
112 | if h > 3000 {
113 | return fmt.Errorf("400 Bad Request: 'height' can be a maximum of 3000.")
114 | }
115 |
116 | if w < 150 {
117 | return fmt.Errorf("400 Bad Request: 'width' needs to be a minimum of 150.")
118 | }
119 |
120 | if w > 3000 {
121 | return fmt.Errorf("400 Bad Request: 'width' can be a maximum of 3000.")
122 | }
123 |
124 | if len(m) > 600 {
125 | return fmt.Errorf("400 Bad Request: Your message is too large, it can be a maximum of 600 bytes.")
126 | }
127 |
128 | if t != "qr" && t != "pdf417" {
129 | return fmt.Errorf("400 Bad Request: Invalid barcode type. Type needs to be: 'qr', 'pdf417'.")
130 | }
131 |
132 | return nil
133 | }
134 |
--------------------------------------------------------------------------------
/main_test.go:
--------------------------------------------------------------------------------
1 | // Testing requires the node package lambda-test
2 | // Download from github link:
3 |
4 | package main
5 |
6 | import (
7 | "aws-lambda-barcode-generator/lib/osutil"
8 | "encoding/json"
9 | "fmt"
10 |
11 | "testing"
12 |
13 | "github.com/stretchr/testify/assert"
14 | )
15 |
16 | type lambdaInputTest struct {
17 | Width interface{} `json:"width,omitempty"`
18 | Height interface{} `json:"height,omitempty"`
19 | Message *string `json:"message,omitempty"`
20 | Type *string `json:"type,omitempty"`
21 | }
22 |
23 | type lambdaOutput struct {
24 | Response *interface{} `json:"response,omitempty"`
25 | Error *string `json:"errorMessage,omitempty"`
26 | }
27 |
28 | // Tests will be called on the compiled binary so ensure we are running the latest version
29 | func TestBuild(t *testing.T) {
30 | assert := assert.New(t)
31 |
32 | _, err := osutil.Run("", "go build")
33 | assert.Nil(err, fmt.Sprintf("Build failed: %v", err))
34 | }
35 |
36 | func TestMissingHeight(t *testing.T) {
37 | width := 200
38 | message := "Test"
39 | ctype := "qr"
40 |
41 | l := lambdaInputTest{
42 | Width: &width,
43 | Message: &message,
44 | Type: &ctype,
45 | }
46 |
47 | resp, err := runLambda(l, 10)
48 |
49 | assert := assert.New(t)
50 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
51 | assert.Equal(*resp.Error, "400 Bad Request: 'height' field is missing.")
52 | }
53 |
54 | func TestMissingWidth(t *testing.T) {
55 | height := 200
56 | message := "Test"
57 | ctype := "qr"
58 |
59 | l := lambdaInputTest{
60 | Height: &height,
61 | Message: &message,
62 | Type: &ctype,
63 | }
64 |
65 | resp, err := runLambda(l, 10)
66 |
67 | assert := assert.New(t)
68 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
69 | assert.Equal(*resp.Error, "400 Bad Request: 'width' field is missing.")
70 | }
71 |
72 | func TestMissingMessage(t *testing.T) {
73 | height := 200
74 | width := 200
75 | ctype := "qr"
76 |
77 | l := lambdaInputTest{
78 | Height: &height,
79 | Width: &width,
80 | Type: &ctype,
81 | }
82 |
83 | resp, err := runLambda(l, 10)
84 |
85 | assert := assert.New(t)
86 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
87 | assert.Equal(*resp.Error, "400 Bad Request: 'message' field is missing.")
88 | }
89 |
90 | func TestMissingType(t *testing.T) {
91 | height := 200
92 | width := 200
93 | message := "Test"
94 |
95 | l := lambdaInputTest{
96 | Height: &height,
97 | Width: &width,
98 | Message: &message,
99 | }
100 |
101 | resp, err := runLambda(l, 10)
102 |
103 | assert := assert.New(t)
104 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
105 | assert.Equal(*resp.Error, "400 Bad Request: 'type' field is missing.")
106 | }
107 |
108 | func TestInvalidType(t *testing.T) {
109 | height := 200
110 | width := 200
111 | message := "Test"
112 | ctype := "test"
113 |
114 | l := lambdaInputTest{
115 | Height: &height,
116 | Width: &width,
117 | Type: &ctype,
118 | Message: &message,
119 | }
120 |
121 | resp, err := runLambda(l, 10)
122 |
123 | assert := assert.New(t)
124 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
125 | assert.Equal(*resp.Error, "400 Bad Request: Invalid barcode type. Type needs to be: 'qr', 'pdf417'.")
126 | }
127 |
128 | func TestInvalidHeight(t *testing.T) {
129 | height := "testnotallowed"
130 | width := 200
131 | message := "Test"
132 | ctype := "qr"
133 |
134 | l := lambdaInputTest{
135 | Height: &height,
136 | Width: &width,
137 | Message: &message,
138 | Type: &ctype,
139 | }
140 |
141 | resp, err := runLambda(l, 10)
142 |
143 | assert := assert.New(t)
144 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
145 | assert.Contains(*resp.Error, "400 Bad Request: Invalid Request - cannot marshal JSON input.")
146 | }
147 |
148 | func TestInvalidWidth(t *testing.T) {
149 | height := 200
150 | width := "testnotallowed"
151 | message := "Test"
152 | ctype := "qr"
153 |
154 | l := lambdaInputTest{
155 | Height: &height,
156 | Width: &width,
157 | Message: &message,
158 | Type: &ctype,
159 | }
160 |
161 | resp, err := runLambda(l, 10)
162 |
163 | assert := assert.New(t)
164 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
165 | assert.Contains(*resp.Error, "400 Bad Request: Invalid Request - cannot marshal JSON input.")
166 | }
167 |
168 | func TestMinimumHeight(t *testing.T) {
169 | height := 149
170 | width := 200
171 | message := "Test"
172 | ctype := "qr"
173 |
174 | l := lambdaInputTest{
175 | Height: &height,
176 | Width: &width,
177 | Message: &message,
178 | Type: &ctype,
179 | }
180 |
181 | resp, err := runLambda(l, 10)
182 |
183 | assert := assert.New(t)
184 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
185 | assert.Equal(*resp.Error, "400 Bad Request: 'height' needs to be a minimum of 150.")
186 | }
187 |
188 | func TestMaximumHeight(t *testing.T) {
189 | height := 3001
190 | width := 200
191 | message := "Test"
192 | ctype := "qr"
193 |
194 | l := lambdaInputTest{
195 | Height: &height,
196 | Width: &width,
197 | Message: &message,
198 | Type: &ctype,
199 | }
200 |
201 | resp, err := runLambda(l, 10)
202 |
203 | assert := assert.New(t)
204 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
205 | assert.Equal(*resp.Error, "400 Bad Request: 'height' can be a maximum of 3000.")
206 | }
207 |
208 | func TestMinimumWidth(t *testing.T) {
209 | height := 200
210 | width := 149
211 | message := "Test"
212 | ctype := "qr"
213 |
214 | l := lambdaInputTest{
215 | Height: &height,
216 | Width: &width,
217 | Message: &message,
218 | Type: &ctype,
219 | }
220 |
221 | resp, err := runLambda(l, 10)
222 |
223 | assert := assert.New(t)
224 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
225 | assert.Equal(*resp.Error, "400 Bad Request: 'width' needs to be a minimum of 150.")
226 | }
227 |
228 | func TestMaximumWidth(t *testing.T) {
229 | height := 200
230 | width := 3001
231 | message := "Test"
232 | ctype := "qr"
233 |
234 | l := lambdaInputTest{
235 | Height: &height,
236 | Width: &width,
237 | Message: &message,
238 | Type: &ctype,
239 | }
240 |
241 | resp, err := runLambda(l, 10)
242 |
243 | assert := assert.New(t)
244 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
245 | assert.Equal(*resp.Error, "400 Bad Request: 'width' can be a maximum of 3000.")
246 | }
247 |
248 | func TestMaximalMessageLength(t *testing.T) {
249 | height := 200
250 | width := 200
251 | message := ""
252 | ctype := "qr"
253 |
254 | for i := 0; i <= 100; i++ {
255 | message += "abcdefgh"
256 | }
257 |
258 | l := lambdaInputTest{
259 | Height: &height,
260 | Width: &width,
261 | Message: &message,
262 | Type: &ctype,
263 | }
264 |
265 | resp, err := runLambda(l, 10)
266 |
267 | assert := assert.New(t)
268 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
269 | assert.Equal(*resp.Error, "400 Bad Request: Your message is too large, it can be a maximum of 600 bytes.")
270 | }
271 |
272 | func TestSuccesfulBarcode(t *testing.T) {
273 | height := 200
274 | width := 600
275 | message := "測試這個"
276 | ctype := "qr"
277 |
278 | l := lambdaInputTest{
279 | Height: &height,
280 | Width: &width,
281 | Message: &message,
282 | Type: &ctype,
283 | }
284 |
285 | runLambda(l, 10)
286 |
287 | /*resp, err := runLambda(l, 10)
288 |
289 | assert := assert.New(t)
290 | assert.Nil(err, fmt.Sprintf("lambda-test returned an unexpected error: %v", err))
291 | assert.Equal(*resp.Error, "400 Bad Request: Your message is too large, it can be a maximum of 600 bytes.")*/
292 |
293 | }
294 |
295 | // Helper method, runs lambda-test and passes in the JSON
296 | func runLambda(request lambdaInputTest, timeout int) (*lambdaOutput, error) {
297 | requestJSON, err := json.Marshal(request)
298 | if err != nil {
299 | return nil, err
300 | }
301 |
302 | response, err := osutil.Run("", fmt.Sprintf("lambda-test -e %v -t %v", string(requestJSON), timeout))
303 | responseObject := &lambdaOutput{}
304 |
305 | if err != nil {
306 | return nil, err
307 | } else {
308 | json.Unmarshal(response, responseObject)
309 | }
310 |
311 | return responseObject, nil
312 | }
313 |
--------------------------------------------------------------------------------