Skip to content

Commit

Permalink
chore: Adding UTs and a bug fix (#10)
Browse files Browse the repository at this point in the history
chore: Adding UTs and a bug fix.

Bug fixes: 
1. For `generate-signature` command the plugin framework was trying to
parse parse input as `verify signature` command input.
2. Removed end of line character for stdout response.

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
  • Loading branch information
priteshbandi committed Jan 19, 2024
1 parent 1d7d81a commit 43178dd
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 57 deletions.
10 changes: 5 additions & 5 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,16 @@ func (c *CLI) Execute(ctx context.Context, args []string) {
resp, err = c.pl.DescribeKey(ctx, &request)
}
case plugin.CommandGenerateSignature:
var request plugin.VerifySignatureRequest
var request plugin.GenerateSignatureRequest
err = c.unmarshalRequest(&request)
if err == nil {
c.logger.Debugf("executing %s plugin's VerifySignature function", reflect.TypeOf(c.pl))
resp, err = c.pl.VerifySignature(ctx, &request)
c.logger.Debugf("executing %s plugin's GenerateSignature function", reflect.TypeOf(c.pl))
resp, err = c.pl.GenerateSignature(ctx, &request)
}
case plugin.Version:
rescueStdOut()
c.printVersion(ctx)
return
default:
// should never happen
rescueStdOut()
Expand All @@ -115,15 +116,14 @@ func (c *CLI) Execute(ctx context.Context, args []string) {
if pluginErr != nil {
deliverError(pluginErr.Error())
}
fmt.Println(op)
fmt.Print(op)
}

// printVersion prints version of executable
func (c *CLI) printVersion(ctx context.Context) {
md := c.getMetadata(ctx, c.pl)

fmt.Printf("%s - %s\nVersion: %s \n", md.Name, md.Description, md.Version)
os.Exit(0)
}

// validateArgs validate commands/arguments passed to executable.
Expand Down
103 changes: 68 additions & 35 deletions cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package cli
import (
"context"
"fmt"
"io"
"log"
"os"
"os/exec"
Expand All @@ -27,7 +28,15 @@ import (
"github.com/notaryproject/notation-plugin-framework-go/plugin"
)

var cli, _ = New(&mockPlugin{})
var cli, _ = New(mock.NewPlugin(false))
var errorCli, _ = New(mock.NewPlugin(true))

func TestNewWithLogger(t *testing.T) {
_, err := NewWithLogger(nil, &discardLogger{})
if err == nil {
t.Fatalf("NewWithLogger() expected error but not found")
}
}

func TestMarshalResponse(t *testing.T) {
res := plugin.GenerateEnvelopeResponse{
Expand Down Expand Up @@ -94,19 +103,10 @@ func TestUnmarshalRequestError(t *testing.T) {
}
}

func TestGetMetadata(t *testing.T) {
pl := mock.NewPlugin(false)
ctx := context.Background()

cli.getMetadata(ctx, pl)
}

func TestGetMetadataError(t *testing.T) {
if os.Getenv("TEST_OS_EXIT") == "1" {
pl := mock.NewPlugin(true)
ctx := context.Background()

cli.getMetadata(ctx, pl)
errorCli.Execute(ctx, []string{string(plugin.CommandGetMetadata)})
return
}
cmd := exec.Command(os.Args[0], "-test.run=TestGetMetadataError")
Expand All @@ -118,6 +118,52 @@ func TestGetMetadataError(t *testing.T) {
t.Fatalf("process ran with err %v, want exit status 1", err)
}

func TestExecuteSuccess(t *testing.T) {
sigGenCli, _ := New(mock.NewSigGeneratorPlugin(false))
tests := map[string]struct {
c *CLI
op string
}{
string(plugin.CommandGetMetadata): {
c: cli,
op: "{\"name\":\"Example Plugin\",\"description\":\"This is an description of example plugin. 🍺\",\"version\":\"1.0.0\",\"url\":\"https://example.com/notation/plugin\",\"capabilities\":[\"SIGNATURE_VERIFIER.TRUSTED_IDENTITY\",\"SIGNATURE_VERIFIER.REVOCATION_CHECK\",\"SIGNATURE_GENERATOR.ENVELOPE\"]}",
},
string(plugin.Version): {
c: cli,
op: "Example Plugin - This is an description of example plugin. 🍺\nVersion: 1.0.0 \n",
},
string(plugin.CommandGenerateEnvelope): {
c: cli,
op: "{\"signatureEnvelope\":\"\",\"signatureEnvelopeType\":\"\",\"annotations\":{\"manifestAnntnKey1\":\"value1\"}}",
},
string(plugin.CommandVerifySignature): {
c: cli,
op: "{\"verificationResults\":{\"SIGNATURE_VERIFIER.REVOCATION_CHECK\":{\"success\":true,\"reason\":\"Not revoked\"},\"SIGNATURE_VERIFIER.TRUSTED_IDENTITY\":{\"success\":true,\"reason\":\"Valid trusted Identity\"}},\"processedAttributes\":[]}",
},
string(plugin.CommandGenerateSignature): {
c: sigGenCli,
op: "{\"keyId\":\"someKeyId\",\"signature\":\"YWJjZA==\",\"signingAlgorithm\":\"RSASSA-PSS-SHA-256\",\"certificateChain\":[\"YWJjZA==\",\"d3h5eg==\"]}",
},
string(plugin.CommandDescribeKey): {
c: sigGenCli,
op: "{\"keyId\":\"someKeyId\",\"keySpec\":\"RSA-2048\"}",
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
closer := setupReader("{}")
defer closer()
op := captureStdOut(func() {
test.c.Execute(context.Background(), []string{"notation", name})
})
fmt.Println(op)
if op != test.op {
t.Errorf("Execute() with '%s' args, expected '%s' but got '%s'", name, test.op, op)
}
})
}
}

func setupReader(content string) func() {
tmpfile, err := os.CreateTemp("", "example")
if err != nil {
Expand All @@ -142,6 +188,17 @@ func setupReader(content string) func() {
}
}

func captureStdOut(f func()) string {
orig := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
f()
os.Stdout = orig
w.Close()
out, _ := io.ReadAll(r)
return string(out)
}

func assertErr(t *testing.T, err error, code plugin.ErrorCode) {
if plgErr, ok := err.(*plugin.Error); ok {
if reflect.DeepEqual(code, plgErr.ErrCode) {
Expand All @@ -152,27 +209,3 @@ func assertErr(t *testing.T, err error, code plugin.ErrorCode) {

t.Errorf("expected error of type PluginError but found %s", reflect.TypeOf(err))
}

type mockPlugin struct {
}

func (p *mockPlugin) DescribeKey(_ context.Context, _ *plugin.DescribeKeyRequest) (*plugin.DescribeKeyResponse, error) {
return nil, plugin.NewUnsupportedError("DescribeKey operation is not implemented by example plugin")
}

func (p *mockPlugin) GenerateSignature(_ context.Context, _ *plugin.GenerateSignatureRequest) (*plugin.GenerateSignatureResponse, error) {
return nil, plugin.NewUnsupportedError("GenerateSignature operation is not implemented by example plugin")
}

func (p *mockPlugin) GenerateEnvelope(_ context.Context, _ *plugin.GenerateEnvelopeRequest) (*plugin.GenerateEnvelopeResponse, error) {
return nil, plugin.NewUnsupportedError("GenerateEnvelope operation is not implemented by example plugin")
}

func (p *mockPlugin) VerifySignature(_ context.Context, _ *plugin.VerifySignatureRequest) (*plugin.VerifySignatureResponse, error) {
return nil, plugin.NewUnsupportedError("VerifySignature operation is not implemented by example plugin")

}

func (p *mockPlugin) GetMetadata(_ context.Context, _ *plugin.GetMetadataRequest) (*plugin.GetMetadataResponse, error) {
return nil, plugin.NewUnsupportedError("GetMetadata operation is not implemented by mock plugin")
}
67 changes: 50 additions & 17 deletions cli/internal/mock/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,60 @@ import (

// Mock plugin used only for testing.
type mockPlugin struct {
fail bool
fail bool
envGenerator bool
}

func NewPlugin(failPlugin bool) *mockPlugin {
return &mockPlugin{fail: failPlugin}
return &mockPlugin{
fail: failPlugin,
envGenerator: true,
}
}

func NewSigGeneratorPlugin(failPlugin bool) *mockPlugin {
return &mockPlugin{
fail: failPlugin,
envGenerator: false,
}
}

func (p *mockPlugin) DescribeKey(ctx context.Context, req *plugin.DescribeKeyRequest) (*plugin.DescribeKeyResponse, error) {
return nil, plugin.NewUnsupportedError("DescribeKey operation is not implemented by example plugin")
if p.fail || p.envGenerator {
return nil, fmt.Errorf("DescribeKey() expected error")
}
return &plugin.DescribeKeyResponse{
KeyID: "someKeyId",
KeySpec: plugin.KeySpecRSA2048,
}, nil
}

func (p *mockPlugin) GenerateSignature(ctx context.Context, req *plugin.GenerateSignatureRequest) (*plugin.GenerateSignatureResponse, error) {
return nil, plugin.NewUnsupportedError("GenerateSignature operation is not implemented by example plugin")
if p.fail || p.envGenerator {
return nil, fmt.Errorf("GenerateSignature() expected error")
}
return &plugin.GenerateSignatureResponse{
KeyID: "someKeyId",
Signature: []byte("abcd"),
SigningAlgorithm: plugin.SignatureAlgorithmRSASSA_PSS_SHA256,
CertificateChain: [][]byte{[]byte("abcd"), []byte("wxyz")},
}, nil
}

func (p *mockPlugin) GenerateEnvelope(ctx context.Context, req *plugin.GenerateEnvelopeRequest) (*plugin.GenerateEnvelopeResponse, error) {
if p.fail {
return nil, fmt.Errorf("expected error")
if p.fail || !p.envGenerator {
return nil, fmt.Errorf("GenerateEnvelope() expected error")
}
return &plugin.GenerateEnvelopeResponse{
SignatureEnvelope: []byte(""),
SignatureEnvelopeType: "application/jose+json",
SignatureEnvelopeType: req.SignatureEnvelopeType,
Annotations: map[string]string{"manifestAnntnKey1": "value1"},
}, nil
}

func (p *mockPlugin) VerifySignature(ctx context.Context, req *plugin.VerifySignatureRequest) (*plugin.VerifySignatureResponse, error) {
if p.fail {
return nil, fmt.Errorf("expected error")
return nil, fmt.Errorf("VerifySignature() expected error")
}
upAttrs := req.Signature.UnprocessedAttributes
pAttrs := make([]interface{}, len(upAttrs))
Expand All @@ -76,16 +101,24 @@ func (p *mockPlugin) VerifySignature(ctx context.Context, req *plugin.VerifySign

func (p *mockPlugin) GetMetadata(ctx context.Context, req *plugin.GetMetadataRequest) (*plugin.GetMetadataResponse, error) {
if p.fail {
return nil, fmt.Errorf("expected error")
return nil, fmt.Errorf("GetMetadata() expected error")
}

cap := []plugin.Capability{
plugin.CapabilityTrustedIdentityVerifier,
plugin.CapabilityRevocationCheckVerifier,
}
if p.envGenerator {
cap = append(cap, plugin.CapabilityEnvelopeGenerator)
} else {
cap = append(cap, plugin.CapabilitySignatureGenerator)
}

return &plugin.GetMetadataResponse{
Name: "Example Plugin",
Description: "This is an description of example plugin. 🍺",
URL: "https://example.com/notation/plugin",
Version: "1.0.0",
Capabilities: []plugin.Capability{
plugin.CapabilityEnvelopeGenerator,
plugin.CapabilityTrustedIdentityVerifier,
plugin.CapabilityRevocationCheckVerifier},
Name: "Example Plugin",
Description: "This is an description of example plugin. 🍺",
URL: "https://example.com/notation/plugin",
Version: "1.0.0",
Capabilities: cap,
}, nil
}

0 comments on commit 43178dd

Please sign in to comment.