Add unit tests for parsing browser requests, enable Travis (#15)

This commit is contained in:
Maxim Baz
2018-04-18 22:52:34 +02:00
committed by GitHub
parent 01a808e2ce
commit d24b54e8b6
7 changed files with 157 additions and 31 deletions

4
.travis.yml Normal file
View File

@@ -0,0 +1,4 @@
language: go
install:
- go get -u github.com/golang/dep/cmd/dep

View File

@@ -25,4 +25,4 @@ browserpass-freebsd64: *.go **/*.go
.PHONY: test
test:
go test
go test ./...

View File

@@ -11,7 +11,7 @@ import (
log "github.com/sirupsen/logrus"
)
func configure(request request) {
func configure(request *request) {
responseData := response.MakeConfigureResponse()
// Check that each and every store in the settings exists and is accessible.

View File

@@ -14,7 +14,7 @@ import (
log "github.com/sirupsen/logrus"
)
func fetchDecryptedContents(request request) {
func fetchDecryptedContents(request *request) {
responseData := response.MakeFetchResponse()
if !strings.HasSuffix(request.File, ".gpg") {
@@ -97,7 +97,7 @@ func fetchDecryptedContents(request request) {
}
}
responseData.Contents, err = decryptFile(store, request.File, gpgPath)
responseData.Contents, err = decryptFile(&store, request.File, gpgPath)
if err != nil {
log.Errorf(
"Unable to decrypt the password file '%v' in the password store '%v' located in '%v': %+v",
@@ -141,7 +141,7 @@ func validateGpgBinary(gpgPath string) error {
return exec.Command(gpgPath, "--version").Run()
}
func decryptFile(store store, file string, gpgPath string) (string, error) {
func decryptFile(store *store, file string, gpgPath string) (string, error) {
passwordFilePath := filepath.Join(store.Path, file)
passwordFile, err := os.Open(passwordFilePath)
if err != nil {

View File

@@ -10,7 +10,7 @@ import (
log "github.com/sirupsen/logrus"
)
func listFiles(request request) {
func listFiles(request *request) {
responseData := response.MakeListResponse()
for _, store := range request.Settings.Stores {

View File

@@ -30,8 +30,29 @@ type request struct {
// Process handles browser request
func Process() {
requestLength := parseRequestLength()
request := parseRequest(requestLength)
requestLength, err := parseRequestLength(os.Stdin)
if err != nil {
log.Error("Unable to parse the length of the browser request: ", err)
response.SendErrorAndExit(
errors.CodeParseRequestLength,
&map[errors.Field]string{
errors.FieldMessage: "Unable to parse the length of the browser request",
errors.FieldError: err.Error(),
},
)
}
request, err := parseRequest(requestLength, os.Stdin)
if err != nil {
log.Error("Unable to parse the browser request: ", err)
response.SendErrorAndExit(
errors.CodeParseRequest,
&map[errors.Field]string{
errors.FieldMessage: "Unable to parse the browser request",
errors.FieldError: err.Error(),
},
)
}
switch request.Action {
case "configure":
@@ -52,35 +73,21 @@ func Process() {
}
}
// Request length is the first 4 bytes in LittleEndian encoding on stdin
func parseRequestLength() uint32 {
// Request length is the first 4 bytes in LittleEndian encoding
func parseRequestLength(input io.Reader) (uint32, error) {
var length uint32
if err := binary.Read(os.Stdin, binary.LittleEndian, &length); err != nil {
log.Error("Unable to parse the length of the browser request: ", err)
response.SendErrorAndExit(
errors.CodeParseRequestLength,
&map[errors.Field]string{
errors.FieldMessage: "Unable to parse the length of the browser request",
errors.FieldError: err.Error(),
},
)
if err := binary.Read(input, binary.LittleEndian, &length); err != nil {
return 0, err
}
return length
return length, nil
}
// Request is a json with a predefined structure
func parseRequest(messageLength uint32) request {
func parseRequest(messageLength uint32, input io.Reader) (*request, error) {
var parsed request
reader := &io.LimitedReader{R: os.Stdin, N: int64(messageLength)}
reader := &io.LimitedReader{R: input, N: int64(messageLength)}
if err := json.NewDecoder(reader).Decode(&parsed); err != nil {
log.Error("Unable to parse the browser request: ", err)
response.SendErrorAndExit(
errors.CodeParseRequest,
&map[errors.Field]string{
errors.FieldMessage: "Unable to parse the browser request",
errors.FieldError: err.Error(),
},
)
return nil, err
}
return parsed
return &parsed, nil
}

115
request/process_test.go Normal file
View File

@@ -0,0 +1,115 @@
package request
import (
"bytes"
"encoding/json"
"io"
"reflect"
"testing"
)
func Test_ParseRequestLength_ConsidersFirstFourBytes(t *testing.T) {
// Arrange
expected := uint32(201334791) // 0x0c002007
// The first 4 bytes represent the value of `expected` in Little Endian format,
// the rest should be completely ignored during the parsing.
input := bytes.NewReader([]byte{7, 32, 0, 12, 13, 13, 13})
// Act
actual, err := parseRequestLength(input)
// Assert
if err != nil {
t.Fatalf("Error parsing request length: %v", err)
}
if expected != actual {
t.Fatalf("The actual length '%v' does not match the expected value of '%v'", actual, expected)
}
}
func Test_ParseRequestLength_ConnectionAborted(t *testing.T) {
// Arrange
expectedErr := io.ErrUnexpectedEOF
input := bytes.NewReader([]byte{7})
// Act
_, err := parseRequestLength(input)
// Assert
if expectedErr != err {
t.Fatalf("The expected error is '%v', but got '%v'", expectedErr, err)
}
}
func Test_ParseRequest_CanParse(t *testing.T) {
// Arrange
expected := &request{
Action: "list",
Settings: settings{
Stores: map[string]store{
"default": store{
Name: "default",
Path: "~/.password-store",
},
},
},
}
jsonBytes, err := json.Marshal(expected)
if err != nil {
t.Fatal("Unable to marshal the expected object to initialize the test")
}
inputLength := uint32(len(jsonBytes))
input := bytes.NewReader(jsonBytes)
// Act
actual, err := parseRequest(inputLength, input)
// Assert
if err != nil {
t.Fatalf("Error parsing request: %v", err)
}
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("The request was parsed incorrectly.\nExpected: %+v\nActual: %+v", expected, actual)
}
}
func Test_ParseRequest_WrongLength(t *testing.T) {
// Arrange
expectedErr := io.ErrUnexpectedEOF
jsonBytes, err := json.Marshal(&request{Action: "list"})
if err != nil {
t.Fatal("Unable to marshal the expected object to initialize the test")
}
wrongInputLength := uint32(len(jsonBytes)) - 1
input := bytes.NewReader(jsonBytes)
// Act
_, err = parseRequest(wrongInputLength, input)
// Assert
if expectedErr != err {
t.Fatalf("The expected error is '%v', but got '%v'", expectedErr, err)
}
}
func Test_ParseRequest_InvalidJson(t *testing.T) {
// Arrange
jsonBytes := []byte("not_a_json")
inputLength := uint32(len(jsonBytes))
input := bytes.NewReader(jsonBytes)
// Act
_, err := parseRequest(inputLength, input)
// Assert
if err == nil {
t.Fatalf("Expected a parsing error, but didn't get it")
}
}