diff --git a/acceptanceTests/cypress.json b/acceptanceTests/cypress.json index d1886cf6e..c43ec18bf 100644 --- a/acceptanceTests/cypress.json +++ b/acceptanceTests/cypress.json @@ -14,7 +14,8 @@ "tests/RegexMasking.js", "tests/IgnoredUserAgents.js", "tests/UiTest.js", - "tests/Redis.js" + "tests/Redis.js", + "tests/Rabbit.js" ], "env": { diff --git a/acceptanceTests/cypress/integration/testHelpers/TrafficHelper.js b/acceptanceTests/cypress/integration/testHelpers/TrafficHelper.js index 925deabcb..cd4a0e757 100644 --- a/acceptanceTests/cypress/integration/testHelpers/TrafficHelper.js +++ b/acceptanceTests/cypress/integration/testHelpers/TrafficHelper.js @@ -1,3 +1,9 @@ +export const valueTabs = { + response: 'RESPONSE', + request: 'REQUEST', + none: null +} + export function isValueExistsInElement(shouldInclude, content, domPathToContainer){ it(`should ${shouldInclude ? '' : 'not'} include '${content}'`, function () { cy.get(domPathToContainer).then(htmlText => { @@ -53,3 +59,114 @@ export function checkThatAllEntriesShown() { cy.get('[title="Fetch old records"]').click(); }); } + +export function checkFilterByMethod(funcDict) { + const {protocol, method, summary} = funcDict; + const summaryDict = getSummeryDict(summary); + const methodDict = getMethodDict(method); + const protocolDict = getProtocolDict(protocol.name, protocol.text); + + it(`Testing the method: ${method}`, function () { + // applying filter + cy.get('.w-tc-editor-text').clear().type(`method == "${method}"`); + cy.get('[type="submit"]').click(); + cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor')); + + cy.get('#entries-length').then(number => { + // if the entries list isn't expanded it expands here + if (number.text() === '0' || number.text() === '1') // todo change when TRA-4262 is fixed + cy.get('[title="Fetch old records"]').click(); + + cy.get('#entries-length').should('not.have.text', '0').and('not.have.text', '1').then(() => { + cy.get(`#list [id]`).then(elements => { + const listElmWithIdAttr = Object.values(elements); + let doneCheckOnFirst = false; + + listElmWithIdAttr.forEach(entry => { + if (entry?.id && entry.id.match(RegExp(/entry-(\d{2}|\d{1})$/gm))) { + const entryNum = getEntryNumById(entry.id); + + leftTextCheck(entryNum, methodDict.pathLeft, methodDict.expectedText); + leftTextCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedTextLeft); + if (summaryDict) + leftTextCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedText); + + if (!doneCheckOnFirst) { + deepCheck(funcDict, protocolDict, methodDict, entry); + doneCheckOnFirst = true; + } + } + }); + }); + }); + }); + }); +} + +function deepCheck(generalDict, protocolDict, methodDict, entry) { + const entryNum = getEntryNumById(entry.id); + const {summary, value} = generalDict; + const summaryDict = getSummeryDict(summary); + + leftOnHoverCheck(entryNum, methodDict.pathLeft, methodDict.expectedOnHover); + leftOnHoverCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedOnHover); + if (summaryDict) + leftOnHoverCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedOnHover); + + cy.get(`#${entry.id}`).click(); + + rightTextCheck(methodDict.pathRight, methodDict.expectedText); + rightTextCheck(protocolDict.pathRight, protocolDict.expectedTextRight); + if (summaryDict) + rightTextCheck(summaryDict.pathRight, summaryDict.expectedText); + + rightOnHoverCheck(methodDict.pathRight, methodDict.expectedOnHover); + rightOnHoverCheck(protocolDict.pathRight, protocolDict.expectedOnHover); + if (summaryDict) + rightOnHoverCheck(summaryDict.pathRight, summaryDict.expectedOnHover); + + if (value) { + if (value.tab === valueTabs.response) + cy.contains('Response').click(); + cy.get(Cypress.env('bodyJsonClass')).then(text => { + expect(text.text()).to.match(value.regex) + }); + } +} + +function getSummeryDict(summary) { + if (summary) { + return { + pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(2) > :nth-child(2)', + pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2) > :nth-child(2)', + expectedText: summary, + expectedOnHover: `summary == "${summary}"` + }; + } + else { + return null; + } +} + +function getMethodDict(method) { + return { + pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2)', + pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(2)', + expectedText: method, + expectedOnHover: `method == "${method}"` + }; +} + +function getProtocolDict(protocol, protocolText) { + return { + pathLeft: '> :nth-child(1) > :nth-child(1)', + pathRight: '> :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(1)', + expectedTextLeft: protocol.toUpperCase(), + expectedTextRight: protocolText, + expectedOnHover: protocol.toLowerCase() + }; +} + +function getEntryNumById (id) { + return parseInt(id.split('-')[1]); +} diff --git a/acceptanceTests/cypress/integration/tests/Rabbit.js b/acceptanceTests/cypress/integration/tests/Rabbit.js new file mode 100644 index 000000000..225266e34 --- /dev/null +++ b/acceptanceTests/cypress/integration/tests/Rabbit.js @@ -0,0 +1,50 @@ +import {checkFilterByMethod, valueTabs,} from "../testHelpers/TrafficHelper"; + + +it('opening mizu', function () { + cy.visit(Cypress.env('testUrl')); +}); + +const rabbitProtocolDetails = {name: 'AMQP', text: 'Advanced Message Queuing Protocol 0-9-1'}; + +checkFilterByMethod({ + protocol: rabbitProtocolDetails, + method: 'exchange declare', + summary: 'exchange', + value: null +}); + +checkFilterByMethod({ + protocol: rabbitProtocolDetails, + method: 'queue declare', + summary: 'queue', + value: null +}); + +checkFilterByMethod({ + protocol: rabbitProtocolDetails, + method: 'queue bind', + summary: 'queue', + value: null +}); + +checkFilterByMethod({ + protocol: rabbitProtocolDetails, + method: 'basic publish', + summary: 'exchange', + value: {tab: valueTabs.request, regex: /^message$/mg} +}); + +checkFilterByMethod({ + protocol: rabbitProtocolDetails, + method: 'basic consume', + summary: 'queue', + value: null +}); + +checkFilterByMethod({ + protocol: rabbitProtocolDetails, + method: 'basic deliver', + summary: 'exchange', + value: {tab: valueTabs.request, regex: /^message$/mg} +}); diff --git a/acceptanceTests/cypress/integration/tests/Redis.js b/acceptanceTests/cypress/integration/tests/Redis.js index 4de6d8aa4..e9ec4ead7 100644 --- a/acceptanceTests/cypress/integration/tests/Redis.js +++ b/acceptanceTests/cypress/integration/tests/Redis.js @@ -1,155 +1,42 @@ -import { - leftOnHoverCheck, - leftTextCheck, - rightOnHoverCheck, - rightTextCheck, -} from "../testHelpers/TrafficHelper"; - -const valueTabs = { - response: 'RESPONSE', - request: 'REQUEST', - none: null -} +import {checkFilterByMethod, valueTabs,} from "../testHelpers/TrafficHelper"; it('opening mizu', function () { cy.visit(Cypress.env('testUrl')); }); -checkRedisFilterByMethod({ +const redisProtocolDetails = {name: 'redis', text: 'Redis Serialization Protocol'}; + +checkFilterByMethod({ + protocol: redisProtocolDetails, method: 'PING', - shouldCheckSummary: false, - valueTab: valueTabs.none -}); + summary: null, + value: null +}) -checkRedisFilterByMethod({ +checkFilterByMethod({ + protocol: redisProtocolDetails, method: 'SET', - shouldCheckSummary: true, - valueTab: valueTabs.request, - valueRegex: /^\[value, keepttl]$/mg -}); + summary: 'key', + value: {tab: valueTabs.request, regex: /^\[value, keepttl]$/mg} +}) -checkRedisFilterByMethod({ +checkFilterByMethod({ + protocol: redisProtocolDetails, method: 'EXISTS', - shouldCheckSummary: true, - valueTab: valueTabs.response, - valueRegex: /^1$/mg -}); + summary: 'key', + value: {tab: valueTabs.response, regex: /^1$/mg} +}) -checkRedisFilterByMethod({ +checkFilterByMethod({ + protocol: redisProtocolDetails, method: 'GET', - shouldCheckSummary: true, - valueTab: valueTabs.response, - valueRegex: /^value$/mg -}); + summary: 'key', + value: {tab: valueTabs.response, regex: /^value$/mg} +}) -checkRedisFilterByMethod({ +checkFilterByMethod({ + protocol: redisProtocolDetails, method: 'DEL', - shouldCheckSummary: true, - valueTab: valueTabs.response, - valueRegex: /^1$|^0$/mg -}); - -function checkRedisFilterByMethod(funcDict) { - const {method, shouldCheckSummary} = funcDict - const summaryDict = getSummeryDict(); - const methodDict = getMethodDict(method); - const protocolDict = getProtocolDict(); - - it(`Testing the method: ${method}`, function () { - // applying filter - cy.get('.w-tc-editor-text').clear().type(`method == "${method}"`); - cy.get('[type="submit"]').click(); - cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor')); - - cy.get('#entries-length').then(number => { - // if the entries list isn't expanded it expands here - if (number.text() === '0' || number.text() === '1') // todo change when TRA-4262 is fixed - cy.get('[title="Fetch old records"]').click(); - - cy.get('#entries-length').should('not.have.text', '0').and('not.have.text', '1').then(() => { - cy.get(`#list [id]`).then(elements => { - const listElmWithIdAttr = Object.values(elements); - let doneCheckOnFirst = false; - - listElmWithIdAttr.forEach(entry => { - if (entry?.id && entry.id.match(RegExp(/entry-(\d{2}|\d{1})$/gm))) { - const entryNum = getEntryNumById(entry.id); - - leftTextCheck(entryNum, methodDict.pathLeft, methodDict.expectedText); - leftTextCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedTextLeft); - if (shouldCheckSummary) - leftTextCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedText); - - if (!doneCheckOnFirst) { - deepCheck(funcDict, protocolDict, methodDict, summaryDict, entry); - doneCheckOnFirst = true; - } - } - }); - }); - }); - }); - }); -} - -function deepCheck(generalDict, protocolDict, methodDict, summaryDict, entry) { - const entryNum = getEntryNumById(entry.id); - const {shouldCheckSummary, valueTab, valueRegex} = generalDict; - - leftOnHoverCheck(entryNum, methodDict.pathLeft, methodDict.expectedOnHover); - leftOnHoverCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedOnHover); - if (shouldCheckSummary) - leftOnHoverCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedOnHover); - - cy.get(`#${entry.id}`).click(); - - rightTextCheck(methodDict.pathRight, methodDict.expectedText); - rightTextCheck(protocolDict.pathRight, protocolDict.expectedTextRight); - if (shouldCheckSummary) - rightTextCheck(summaryDict.pathRight, summaryDict.expectedText); - - rightOnHoverCheck(methodDict.pathRight, methodDict.expectedOnHover); - rightOnHoverCheck(protocolDict.pathRight, protocolDict.expectedOnHover); - if (shouldCheckSummary) - rightOnHoverCheck(summaryDict.pathRight, summaryDict.expectedOnHover); - - if (valueTab) { - if (valueTab === valueTabs.response) - cy.contains('Response').click(); - cy.get(Cypress.env('bodyJsonClass')).then(text => { - expect(text.text()).to.match(valueRegex) - }); - } -} - -function getSummeryDict() { - return { - pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(2) > :nth-child(2)', - pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2) > :nth-child(2)', - expectedText: 'key', - expectedOnHover: `summary == "key"` - }; -} - -function getMethodDict(method) { - return { - pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2)', - pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(2)', - expectedText: method, - expectedOnHover: `method == "${method}"` - }; -} - -function getProtocolDict() { - return { - pathLeft: '> :nth-child(1) > :nth-child(1)', - pathRight: '> :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(1)', - expectedTextLeft: 'REDIS', - expectedTextRight: 'Redis Serialization Protocol', - expectedOnHover: `redis` - }; -} - -function getEntryNumById (id) { - return parseInt(id.split('-')[1]); -} + summary: 'key', + value: {tab: valueTabs.response, regex: /^1$|^0$/mg} +}) diff --git a/acceptanceTests/extensions_test.go b/acceptanceTests/extensions_test.go index 9be121b1e..76ee44b1a 100644 --- a/acceptanceTests/extensions_test.go +++ b/acceptanceTests/extensions_test.go @@ -4,8 +4,10 @@ import ( "context" "fmt" "github.com/go-redis/redis/v8" + amqp "github.com/rabbitmq/amqp091-go" "os/exec" "testing" + "time" ) func TestRedis(t *testing.T) { @@ -99,3 +101,128 @@ func TestRedis(t *testing.T) { runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/Redis.js\"") } + +func TestAmqp(t *testing.T) { + if testing.Short() { + t.Skip("ignored acceptance test") + } + + cliPath, cliPathErr := getCliPath() + if cliPathErr != nil { + t.Errorf("failed to get cli path, err: %v", cliPathErr) + return + } + + tapCmdArgs := getDefaultTapCommandArgs() + + tapNamespace := getDefaultTapNamespace() + tapCmdArgs = append(tapCmdArgs, tapNamespace...) + + tapCmd := exec.Command(cliPath, tapCmdArgs...) + t.Logf("running command: %v", tapCmd.String()) + + t.Cleanup(func() { + if err := cleanupCommand(tapCmd); err != nil { + t.Logf("failed to cleanup tap command, err: %v", err) + } + }) + + if err := tapCmd.Start(); err != nil { + t.Errorf("failed to start tap command, err: %v", err) + return + } + + apiServerUrl := getApiServerUrl(defaultApiServerPort) + + if err := waitTapPodsReady(apiServerUrl); err != nil { + t.Errorf("failed to start tap pods on time, err: %v", err) + return + } + + ctx := context.Background() + + rabbitmqExternalIp, err := getServiceExternalIp(ctx, defaultNamespaceName, "rabbitmq") + if err != nil { + t.Errorf("failed to get RabbitMQ external ip, err: %v", err) + return + } + + conn, err := amqp.Dial(fmt.Sprintf("amqp://guest:guest@%v:5672/", rabbitmqExternalIp)) + if err != nil { + t.Errorf("failed to connect to RabbitMQ, err: %v", err) + return + } + defer conn.Close() + + // Temporary fix for missing amqp entries + time.Sleep(5 * time.Second) + + for i := 0; i < defaultEntriesCount/5; i++ { + ch, err := conn.Channel() + if err != nil { + t.Errorf("failed to open a channel, err: %v", err) + return + } + + exchangeName := "exchange" + err = ch.ExchangeDeclare(exchangeName, "direct", true, false, false, false, nil) + if err != nil { + t.Errorf("failed to declare an exchange, err: %v", err) + return + } + + q, err := ch.QueueDeclare("queue", true, false, false, false, nil) + if err != nil { + t.Errorf("failed to declare a queue, err: %v", err) + return + } + + routingKey := "routing_key" + err = ch.QueueBind(q.Name, routingKey, exchangeName, false, nil) + if err != nil { + t.Errorf("failed to bind the queue, err: %v", err) + return + } + + err = ch.Publish(exchangeName, routingKey, false, false, + amqp.Publishing{ + DeliveryMode: amqp.Persistent, + ContentType: "text/plain", + Body: []byte("message"), + }) + if err != nil { + t.Errorf("failed to publish a message, err: %v", err) + return + } + + msgChan, err := ch.Consume(q.Name, "Consumer", true, false, false, false, nil) + if err != nil { + t.Errorf("failed to create a consumer, err: %v", err) + return + } + + select { + case <-msgChan: + break + case <-time.After(3 * time.Second): + t.Errorf("failed to consume a message on time") + return + } + + err = ch.ExchangeDelete(exchangeName, false, false) + if err != nil { + t.Errorf("failed to delete the exchange, err: %v", err) + return + } + + _, err = ch.QueueDelete(q.Name, false, false, false) + if err != nil { + t.Errorf("failed to delete the queue, err: %v", err) + return + } + + ch.Close() + } + + runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/Rabbit.js\"") +} diff --git a/acceptanceTests/go.mod b/acceptanceTests/go.mod index ca117e74e..f8183a783 100644 --- a/acceptanceTests/go.mod +++ b/acceptanceTests/go.mod @@ -4,6 +4,7 @@ go 1.17 require ( github.com/go-redis/redis/v8 v8.11.4 + github.com/rabbitmq/amqp091-go v1.3.0 github.com/up9inc/mizu/shared v0.0.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b k8s.io/apimachinery v0.23.3 diff --git a/acceptanceTests/go.sum b/acceptanceTests/go.sum index 6d489026f..8c4d2a40e 100644 --- a/acceptanceTests/go.sum +++ b/acceptanceTests/go.sum @@ -427,6 +427,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rabbitmq/amqp091-go v1.3.0 h1:A/QuHiNw7LMCJsxx9iZn5lrIz6OrhIn7Dfk5/1YatWM= +github.com/rabbitmq/amqp091-go v1.3.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= diff --git a/acceptanceTests/setup.sh b/acceptanceTests/setup.sh index 64d833537..3153569c2 100644 --- a/acceptanceTests/setup.sh +++ b/acceptanceTests/setup.sh @@ -39,6 +39,9 @@ kubectl create deployment httpbin --image=kennethreitz/httpbin -n mizu-tests2 echo "Creating redis deployment" kubectl create deployment redis --image=redis -n mizu-tests +echo "Creating rabbitmq deployment" +kubectl create deployment rabbitmq --image=rabbitmq -n mizu-tests + echo "Creating httpbin services" kubectl expose deployment httpbin --type=NodePort --port=80 -n mizu-tests kubectl expose deployment httpbin2 --type=NodePort --port=80 -n mizu-tests @@ -48,6 +51,9 @@ kubectl expose deployment httpbin --type=NodePort --port=80 -n mizu-tests2 echo "Creating redis service" kubectl expose deployment redis --type=LoadBalancer --port=6379 -n mizu-tests +echo "Creating rabbitmq service" +kubectl expose deployment rabbitmq --type=LoadBalancer --port=5672 -n mizu-tests + echo "Starting proxy" kubectl proxy --port=8080 &