Files
probo/e2e/console/employee_document_test.go
Émile Ré 9156d6a16a Add wsl linter and fix
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-20 09:27:28 +04:00

1155 lines
29 KiB
Go

// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package console_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.probo.inc/probo/e2e/internal/testutil"
)
func TestEmployeeDocument_NodeAccess(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
employee := testutil.NewClientInOrg(t, testutil.RoleEmployee, owner)
docID, _ := createTestDocument(t, owner)
t.Run("employee cannot list documents", func(t *testing.T) {
t.Parallel()
_, err := employee.Do(`
query ListDocuments($orgId: ID!) {
node(id: $orgId) {
... on Organization {
documents(first: 10) {
edges { node { id } }
}
}
}
}
`, map[string]any{"orgId": employee.GetOrganizationID().String()})
testutil.RequireForbiddenError(t, err, "employee should not list documents")
})
t.Run("employee cannot access document via node query", func(t *testing.T) {
t.Parallel()
_, err := employee.Do(`
query GetDocument($id: ID!) {
node(id: $id) {
... on Document {
id
}
}
}
`, map[string]any{"id": docID})
testutil.RequireForbiddenError(t, err, "employee should not access document via node")
})
t.Run("owner can access document via node query", func(t *testing.T) {
t.Parallel()
_, err := owner.Do(`
query GetDocument($id: ID!) {
node(id: $id) {
... on Document {
id
}
}
}
`, map[string]any{"id": docID})
require.NoError(t, err, "owner should access document via node")
})
t.Run("employee cannot access signableDocument they are not signer of", func(t *testing.T) {
t.Parallel()
_, err := employee.Do(`
query($docId: ID!) {
viewer {
signableDocument(id: $docId) {
id
}
}
}
`, map[string]any{"docId": docID})
testutil.RequireErrorCode(t, err, "NOT_FOUND", "employee should not access signableDocument for doc they are not signer of")
})
t.Run("employee cannot access approvableDocument they are not approver of", func(t *testing.T) {
t.Parallel()
_, err := employee.Do(`
query($docId: ID!) {
viewer {
approvableDocument(id: $docId) {
id
}
}
}
`, map[string]any{"docId": docID})
testutil.RequireErrorCode(t, err, "NOT_FOUND", "employee should not access approvableDocument for doc they are not approver of")
})
}
func TestEmployeeDocument_ExportPDF(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
employee := testutil.NewClientInOrg(t, testutil.RoleEmployee, owner)
_, docVersionID := createTestDocument(t, owner)
t.Run("employee cannot use exportDocumentVersionPDF", func(t *testing.T) {
t.Parallel()
_, err := employee.Do(`
mutation ExportPDF($input: ExportDocumentVersionPDFInput!) {
exportDocumentVersionPDF(input: $input) {
data
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": docVersionID,
"withWatermark": false,
"withSignatures": false,
},
})
testutil.RequireForbiddenError(t, err, "employee should not use exportDocumentVersionPDF")
})
t.Run("owner can use exportDocumentVersionPDF", func(t *testing.T) {
t.Parallel()
_, err := owner.Do(`
mutation ExportPDF($input: ExportDocumentVersionPDFInput!) {
exportDocumentVersionPDF(input: $input) {
data
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": docVersionID,
"withWatermark": false,
"withSignatures": false,
},
})
require.NoError(t, err, "owner should use exportDocumentVersionPDF")
})
t.Run("employee cannot use exportEmployeeDocumentVersionPDF without being signer", func(t *testing.T) {
t.Parallel()
_, err := employee.Do(`
mutation ExportEmployeePDF($input: ExportEmployeeDocumentVersionPDFInput!) {
exportEmployeeDocumentVersionPDF(input: $input) {
data
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": docVersionID,
},
})
testutil.RequireErrorCode(t, err, "NOT_FOUND", "employee should not export PDF for document they are not signer of")
})
}
func TestEmployeeDocument_SignableDocuments(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
employee := testutil.NewClientInOrg(t, testutil.RoleEmployee, owner)
t.Run("employee can list signable documents", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
SignableDocuments struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"signableDocuments"`
} `json:"viewer"`
}
err := employee.Execute(`
query($orgId: ID!) {
viewer {
signableDocuments(organizationId: $orgId, first: 10) {
edges { node { id } }
}
}
}
`, map[string]any{"orgId": employee.GetOrganizationID().String()}, &result)
require.NoError(t, err, "employee should list signable documents")
})
t.Run("employee can list approvable documents", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
ApprovableDocuments struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"approvableDocuments"`
} `json:"viewer"`
}
err := employee.Execute(`
query($orgId: ID!) {
viewer {
approvableDocuments(organizationId: $orgId, first: 10) {
edges { node { id } }
}
}
}
`, map[string]any{"orgId": employee.GetOrganizationID().String()}, &result)
require.NoError(t, err, "employee should list approvable documents")
})
t.Run("employee does not see document they are not signer of in signableDocuments", func(t *testing.T) {
t.Parallel()
// Create a document with signature requested from owner only
docID, _ := createTestDocument(t, owner)
approveTestDocument(t, owner, docID)
var versionResult struct {
Node struct {
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"node"`
}
err := owner.Execute(`
query($id: ID!) {
node(id: $id) {
... on Document {
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
edges { node { id } }
}
}
}
}
`, map[string]any{"id": docID}, &versionResult)
require.NoError(t, err)
require.NotEmpty(t, versionResult.Node.Versions.Edges)
ownerProfileID := owner.GetProfileID().String()
_, err = owner.Do(`
mutation($input: RequestSignatureInput!) {
requestSignature(input: $input) {
documentVersionSignatureEdge { node { id } }
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": versionResult.Node.Versions.Edges[0].Node.ID,
"signatoryId": ownerProfileID,
},
})
require.NoError(t, err)
// Employee should not see this document
var listResult struct {
Viewer struct {
SignableDocuments struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"signableDocuments"`
} `json:"viewer"`
}
err = employee.Execute(`
query($orgId: ID!) {
viewer {
signableDocuments(organizationId: $orgId, first: 100) {
edges { node { id } }
}
}
}
`, map[string]any{"orgId": employee.GetOrganizationID().String()}, &listResult)
require.NoError(t, err)
for _, edge := range listResult.Viewer.SignableDocuments.Edges {
assert.NotEqual(t, docID, edge.Node.ID, "employee should not see document they are not signer of")
}
})
t.Run("employee does not see document they are not approver of in approvableDocuments", func(t *testing.T) {
t.Parallel()
// Create a document with approval requested from owner only
docID, _ := createTestDocument(t, owner)
ownerProfileID := owner.GetProfileID().String()
_, err := owner.Do(`
mutation($input: PublishDocumentInput!) {
publishDocument(input: $input) {
approvalQuorum { id }
}
}
`, map[string]any{
"input": map[string]any{
"minor": false,
"documentId": docID,
"approverIds": []string{ownerProfileID},
"changelog": "Test changelog",
},
})
require.NoError(t, err)
// Employee should not see this document
var listResult struct {
Viewer struct {
ApprovableDocuments struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"approvableDocuments"`
} `json:"viewer"`
}
err = employee.Execute(`
query($orgId: ID!) {
viewer {
approvableDocuments(organizationId: $orgId, first: 100) {
edges { node { id } }
}
}
}
`, map[string]any{"orgId": employee.GetOrganizationID().String()}, &listResult)
require.NoError(t, err)
for _, edge := range listResult.Viewer.ApprovableDocuments.Edges {
assert.NotEqual(t, docID, edge.Node.ID, "employee should not see document they are not approver of")
}
})
}
func TestEmployeeDocument_FilterModeIsolation(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
admin := testutil.NewClientInOrg(t, testutil.RoleAdmin, owner)
// Create a document and have the admin approve it (so owner is NOT an approver)
docID, _ := createTestDocument(t, owner)
adminProfileID := admin.GetProfileID().String()
_, err := owner.Do(`
mutation($input: PublishDocumentInput!) {
publishDocument(input: $input) {
approvalQuorum { id }
}
}
`, map[string]any{
"input": map[string]any{
"minor": false,
"documentId": docID,
"approverIds": []string{adminProfileID},
"changelog": "Test changelog",
},
})
require.NoError(t, err)
// Get the version ID created by the approval request
var approveVersionResult struct {
Node struct {
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"node"`
}
err = owner.Execute(`
query($id: ID!) {
node(id: $id) {
... on Document {
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
edges { node { id } }
}
}
}
}
`, map[string]any{"id": docID}, &approveVersionResult)
require.NoError(t, err)
require.NotEmpty(t, approveVersionResult.Node.Versions.Edges)
pendingVersionID := approveVersionResult.Node.Versions.Edges[0].Node.ID
_, err = admin.Do(`
mutation($input: ApproveDocumentVersionInput!) {
approveDocumentVersion(input: $input) {
approvalDecision { id }
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": pendingVersionID,
},
})
require.NoError(t, err)
// Get the published version ID
var versionResult struct {
Node struct {
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"node"`
}
err = owner.Execute(`
query($id: ID!) {
node(id: $id) {
... on Document {
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
edges { node { id } }
}
}
}
}
`, map[string]any{"id": docID}, &versionResult)
require.NoError(t, err)
require.NotEmpty(t, versionResult.Node.Versions.Edges)
publishedVersionID := versionResult.Node.Versions.Edges[0].Node.ID
ownerProfileID := owner.GetProfileID().String()
// Request signature from the owner only (owner is a signer but NOT an approver)
_, err = owner.Do(`
mutation($input: RequestSignatureInput!) {
requestSignature(input: $input) {
documentVersionSignatureEdge {
node { id }
}
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": publishedVersionID,
"signatoryId": ownerProfileID,
},
})
require.NoError(t, err)
t.Run("signer sees document in signableDocuments list", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
SignableDocuments struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"signableDocuments"`
} `json:"viewer"`
}
err := owner.Execute(`
query($orgId: ID!) {
viewer {
signableDocuments(organizationId: $orgId, first: 10) {
edges { node { id } }
}
}
}
`, map[string]any{"orgId": owner.GetOrganizationID().String()}, &result)
require.NoError(t, err)
var found bool
for _, edge := range result.Viewer.SignableDocuments.Edges {
if edge.Node.ID == docID {
found = true
break
}
}
assert.True(t, found, "signer should see document in signableDocuments list")
})
t.Run("signer does not see document in approvableDocuments list", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
ApprovableDocuments struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"approvableDocuments"`
} `json:"viewer"`
}
err := owner.Execute(`
query($orgId: ID!) {
viewer {
approvableDocuments(organizationId: $orgId, first: 10) {
edges { node { id } }
}
}
}
`, map[string]any{"orgId": owner.GetOrganizationID().String()}, &result)
require.NoError(t, err)
for _, edge := range result.Viewer.ApprovableDocuments.Edges {
assert.NotEqual(t, docID, edge.Node.ID, "signer-only document should not appear in approvableDocuments list")
}
})
t.Run("signer cannot access document via approvableDocument", func(t *testing.T) {
t.Parallel()
_, err := owner.Do(`
query($docId: ID!) {
viewer {
approvableDocument(id: $docId) {
id
}
}
}
`, map[string]any{"docId": docID})
testutil.RequireErrorCode(t, err, "NOT_FOUND", "signer-only should not access approvableDocument")
})
}
func TestEmployeeDocument_ApproverFilterModeIsolation(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
// Create a document and request approval (owner is approver but NOT a signer)
docID, _ := createTestDocument(t, owner)
ownerProfileID := owner.GetProfileID().String()
_, err := owner.Do(`
mutation($input: PublishDocumentInput!) {
publishDocument(input: $input) {
approvalQuorum { id }
}
}
`, map[string]any{
"input": map[string]any{
"minor": false,
"documentId": docID,
"approverIds": []string{ownerProfileID},
"changelog": "Test changelog",
},
})
require.NoError(t, err)
t.Run("approver sees document in approvableDocuments list", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
ApprovableDocuments struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"approvableDocuments"`
} `json:"viewer"`
}
err := owner.Execute(`
query($orgId: ID!) {
viewer {
approvableDocuments(organizationId: $orgId, first: 10) {
edges { node { id } }
}
}
}
`, map[string]any{"orgId": owner.GetOrganizationID().String()}, &result)
require.NoError(t, err)
var found bool
for _, edge := range result.Viewer.ApprovableDocuments.Edges {
if edge.Node.ID == docID {
found = true
break
}
}
assert.True(t, found, "approver should see document in approvableDocuments list")
})
t.Run("approver does not see document in signableDocuments list", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
SignableDocuments struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"signableDocuments"`
} `json:"viewer"`
}
err := owner.Execute(`
query($orgId: ID!) {
viewer {
signableDocuments(organizationId: $orgId, first: 10) {
edges { node { id } }
}
}
}
`, map[string]any{"orgId": owner.GetOrganizationID().String()}, &result)
require.NoError(t, err)
for _, edge := range result.Viewer.SignableDocuments.Edges {
assert.NotEqual(t, docID, edge.Node.ID, "approver-only document should not appear in signableDocuments list")
}
})
t.Run("approver cannot access document via signableDocument", func(t *testing.T) {
t.Parallel()
_, err := owner.Do(`
query($docId: ID!) {
viewer {
signableDocument(id: $docId) {
id
}
}
}
`, map[string]any{"docId": docID})
testutil.RequireErrorCode(t, err, "NOT_FOUND", "approver-only should not access signableDocument")
})
}
func TestEmployeeDocument_UnsignedDocument(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
// Create a document, approve and publish it, then request signature from owner
docID, _ := createTestDocument(t, owner)
approveTestDocument(t, owner, docID)
var versionResult struct {
Node struct {
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"node"`
}
err := owner.Execute(`
query($id: ID!) {
node(id: $id) {
... on Document {
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
edges { node { id } }
}
}
}
}
`, map[string]any{"id": docID}, &versionResult)
require.NoError(t, err)
require.NotEmpty(t, versionResult.Node.Versions.Edges)
publishedVersionID := versionResult.Node.Versions.Edges[0].Node.ID
ownerProfileID := owner.GetProfileID().String()
_, err = owner.Do(`
mutation($input: RequestSignatureInput!) {
requestSignature(input: $input) {
documentVersionSignatureEdge { node { id } }
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": publishedVersionID,
"signatoryId": ownerProfileID,
},
})
require.NoError(t, err)
t.Run("unsigned document shows signed=false on version", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
SignableDocument struct {
ID string `json:"id"`
Signed *bool `json:"signed"`
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
Signed bool `json:"signed"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"signableDocument"`
} `json:"viewer"`
}
err := owner.Execute(`
query($docId: ID!) {
viewer {
signableDocument(id: $docId) {
id
signed
versions(first: 10) {
edges {
node {
id
signed
}
}
}
}
}
}
`, map[string]any{"docId": docID}, &result)
require.NoError(t, err)
require.NotNil(t, result.Viewer.SignableDocument.Signed)
assert.False(t, *result.Viewer.SignableDocument.Signed, "document should not be signed yet")
require.NotEmpty(t, result.Viewer.SignableDocument.Versions.Edges)
assert.False(t, result.Viewer.SignableDocument.Versions.Edges[0].Node.Signed, "version should not be signed yet")
})
t.Run("unsigned document still allows exportEmployeeDocumentVersionPDF", func(t *testing.T) {
t.Parallel()
_, err := owner.Do(`
mutation($input: ExportEmployeeDocumentVersionPDFInput!) {
exportEmployeeDocumentVersionPDF(input: $input) {
data
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": publishedVersionID,
},
})
require.NoError(t, err, "signer should export PDF even before signing")
})
}
func TestEmployeeDocument_UnapprovedDocument(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
// Create a document and request approval from owner (don't approve yet)
docID, _ := createTestDocument(t, owner)
ownerProfileID := owner.GetProfileID().String()
_, err := owner.Do(`
mutation($input: PublishDocumentInput!) {
publishDocument(input: $input) {
approvalQuorum { id }
}
}
`, map[string]any{
"input": map[string]any{
"minor": false,
"documentId": docID,
"approverIds": []string{ownerProfileID},
"changelog": "Test changelog",
},
})
require.NoError(t, err)
t.Run("unapproved document shows PENDING state", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
ApprovableDocument struct {
ID string `json:"id"`
ApprovalState *string `json:"approvalState"`
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
ApprovalDecision *struct {
ID string `json:"id"`
State string `json:"state"`
} `json:"approvalDecision"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"approvableDocument"`
} `json:"viewer"`
}
err := owner.Execute(`
query($docId: ID!) {
viewer {
approvableDocument(id: $docId) {
id
approvalState
versions(first: 10) {
edges {
node {
id
approvalDecision {
id
state
}
}
}
}
}
}
}
`, map[string]any{"docId": docID}, &result)
require.NoError(t, err)
require.NotNil(t, result.Viewer.ApprovableDocument.ApprovalState)
assert.Equal(t, "PENDING", *result.Viewer.ApprovableDocument.ApprovalState, "approval state should be PENDING")
require.NotEmpty(t, result.Viewer.ApprovableDocument.Versions.Edges)
require.NotNil(t, result.Viewer.ApprovableDocument.Versions.Edges[0].Node.ApprovalDecision)
assert.Equal(t, "PENDING", result.Viewer.ApprovableDocument.Versions.Edges[0].Node.ApprovalDecision.State, "decision should be PENDING")
})
t.Run("unapproved document still allows exportEmployeeDocumentVersionPDF", func(t *testing.T) {
t.Parallel()
var versionResult struct {
Node struct {
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"node"`
}
err := owner.Execute(`
query($id: ID!) {
node(id: $id) {
... on Document {
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
edges { node { id } }
}
}
}
}
`, map[string]any{"id": docID}, &versionResult)
require.NoError(t, err)
require.NotEmpty(t, versionResult.Node.Versions.Edges)
_, err = owner.Do(`
mutation($input: ExportEmployeeDocumentVersionPDFInput!) {
exportEmployeeDocumentVersionPDF(input: $input) {
data
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": versionResult.Node.Versions.Edges[0].Node.ID,
},
})
require.NoError(t, err, "approver should export PDF even before approving")
})
}
func TestEmployeeDocument_SignableDocumentNestedFields(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
// Create a document, approve and publish it
docID, _ := createTestDocument(t, owner)
approveTestDocument(t, owner, docID)
// Get the published version ID
var versionResult struct {
Node struct {
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"node"`
}
err := owner.Execute(`
query($id: ID!) {
node(id: $id) {
... on Document {
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
edges { node { id } }
}
}
}
}
`, map[string]any{"id": docID}, &versionResult)
require.NoError(t, err)
require.NotEmpty(t, versionResult.Node.Versions.Edges)
publishedVersionID := versionResult.Node.Versions.Edges[0].Node.ID
// Request signature from the owner
ownerProfileID := owner.GetProfileID().String()
_, err = owner.Do(`
mutation($input: RequestSignatureInput!) {
requestSignature(input: $input) {
documentVersionSignatureEdge {
node { id state }
}
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": publishedVersionID,
"signatoryId": ownerProfileID,
},
})
require.NoError(t, err)
t.Run("owner can access signableDocument with nested fields", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
SignableDocument struct {
ID string `json:"id"`
Title string `json:"title"`
Signed *bool `json:"signed"`
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
Signed bool `json:"signed"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"signableDocument"`
} `json:"viewer"`
}
err := owner.Execute(`
query($docId: ID!) {
viewer {
signableDocument(id: $docId) {
id
title
signed
versions(first: 10) {
edges {
node {
id
signed
}
}
}
}
}
}
`, map[string]any{"docId": docID}, &result)
require.NoError(t, err, "owner should access signableDocument with nested fields")
assert.NotEmpty(t, result.Viewer.SignableDocument.ID)
assert.NotNil(t, result.Viewer.SignableDocument.Signed)
assert.NotEmpty(t, result.Viewer.SignableDocument.Versions.Edges)
})
t.Run("owner can use exportEmployeeDocumentVersionPDF as signer", func(t *testing.T) {
t.Parallel()
_, err := owner.Do(`
mutation($input: ExportEmployeeDocumentVersionPDFInput!) {
exportEmployeeDocumentVersionPDF(input: $input) {
data
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": publishedVersionID,
},
})
require.NoError(t, err, "owner signer should use exportEmployeeDocumentVersionPDF")
})
}
func TestEmployeeDocument_ApprovableDocumentNestedFields(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
// Create a document and request approval with the owner as approver
docID, _ := createTestDocument(t, owner)
ownerProfileID := owner.GetProfileID().String()
_, err := owner.Do(`
mutation($input: PublishDocumentInput!) {
publishDocument(input: $input) {
approvalQuorum { id }
}
}
`, map[string]any{
"input": map[string]any{
"minor": false,
"documentId": docID,
"approverIds": []string{ownerProfileID},
"changelog": "Test changelog",
},
})
require.NoError(t, err)
t.Run("owner can access approvableDocument with nested fields", func(t *testing.T) {
t.Parallel()
var result struct {
Viewer struct {
ApprovableDocument struct {
ID string `json:"id"`
Title string `json:"title"`
ApprovalState *string `json:"approvalState"`
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
ApprovalDecision *struct {
ID string `json:"id"`
State string `json:"state"`
} `json:"approvalDecision"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"approvableDocument"`
} `json:"viewer"`
}
err := owner.Execute(`
query($docId: ID!) {
viewer {
approvableDocument(id: $docId) {
id
title
approvalState
versions(first: 10) {
edges {
node {
id
approvalDecision {
id
state
}
}
}
}
}
}
}
`, map[string]any{"docId": docID}, &result)
require.NoError(t, err, "owner should access approvableDocument with nested fields")
assert.NotEmpty(t, result.Viewer.ApprovableDocument.ID)
assert.NotEmpty(t, result.Viewer.ApprovableDocument.Versions.Edges)
})
t.Run("owner can use exportEmployeeDocumentVersionPDF as approver", func(t *testing.T) {
t.Parallel()
// Get the version ID
var versionResult struct {
Node struct {
Versions struct {
Edges []struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"edges"`
} `json:"versions"`
} `json:"node"`
}
err := owner.Execute(`
query($id: ID!) {
node(id: $id) {
... on Document {
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
edges { node { id } }
}
}
}
}
`, map[string]any{"id": docID}, &versionResult)
require.NoError(t, err)
require.NotEmpty(t, versionResult.Node.Versions.Edges)
versionID := versionResult.Node.Versions.Edges[0].Node.ID
_, err = owner.Do(`
mutation($input: ExportEmployeeDocumentVersionPDFInput!) {
exportEmployeeDocumentVersionPDF(input: $input) {
data
}
}
`, map[string]any{
"input": map[string]any{
"documentVersionId": versionID,
},
})
require.NoError(t, err, "owner approver should use exportEmployeeDocumentVersionPDF")
})
}