From 1847027b35a29970023eadae8b0f8027b4bee893 Mon Sep 17 00:00:00 2001 From: slayerjain Date: Fri, 20 Mar 2026 21:19:03 +0530 Subject: [PATCH] feat: add dns-dedup E2E sample app HTTP server that triggers many DNS lookups for the same domain, testing that Keploy properly deduplicates DNS mocks when a domain returns different IPs on each lookup (e.g., AWS SQS round-robin). --- dns-dedup/curl.sh | 25 +++++++++++ dns-dedup/go.mod | 3 ++ dns-dedup/main.go | 103 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100755 dns-dedup/curl.sh create mode 100644 dns-dedup/go.mod create mode 100644 dns-dedup/main.go diff --git a/dns-dedup/curl.sh b/dns-dedup/curl.sh new file mode 100755 index 00000000..43ed2427 --- /dev/null +++ b/dns-dedup/curl.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Traffic generation for dns-dedup E2E test. +# Exercises single and bulk DNS resolution endpoints. + +set -euo pipefail + +BASE="http://localhost:8086" + +echo "=== Single resolve (default domain) ===" +curl -sS "$BASE/resolve" +echo + +echo "=== Single resolve (google.com) ===" +curl -sS "$BASE/resolve?domain=google.com" +echo + +echo "=== Bulk resolve: 30 lookups for default domain ===" +curl -sS "$BASE/resolve-many?n=30" +echo + +echo "=== Bulk resolve: 10 lookups for google.com ===" +curl -sS "$BASE/resolve-many?n=10&domain=google.com" +echo + +echo "=== Done ===" diff --git a/dns-dedup/go.mod b/dns-dedup/go.mod new file mode 100644 index 00000000..c7e455a2 --- /dev/null +++ b/dns-dedup/go.mod @@ -0,0 +1,3 @@ +module dns-dedup + +go 1.22.0 diff --git a/dns-dedup/main.go b/dns-dedup/main.go new file mode 100644 index 00000000..5951961a --- /dev/null +++ b/dns-dedup/main.go @@ -0,0 +1,103 @@ +package main + +import ( + "encoding/json" + "fmt" + "net" + "net/http" + "os" + "strconv" + "time" +) + +// This app tests that Keploy properly deduplicates DNS mocks when the same +// domain returns different IPs on each lookup (round-robin / load-balancing). +// +// AWS services like SQS rotate IPs per DNS query. Before the fix, Keploy +// recorded a new DNS mock for every unique IP set, resulting in thousands of +// duplicate DNS mocks for a single domain. +// +// The /resolve-many endpoint triggers many DNS lookups for the same domain, +// which is the key scenario for verifying deduplication. + +func main() { + domain := "sqs.us-east-1.amazonaws.com" + if len(os.Args) > 1 { + domain = os.Args[1] + } + + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, "ok") + }) + + // Single DNS lookup + http.HandleFunc("/resolve", func(w http.ResponseWriter, r *http.Request) { + d := r.URL.Query().Get("domain") + if d == "" { + d = domain + } + ips, err := net.LookupHost(d) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]interface{}{ + "domain": d, + "ips": ips, + }) + }) + + // Many DNS lookups for the same domain — the key dedup scenario. + // Without dedup, each unique IP set becomes a separate DNS mock. + http.HandleFunc("/resolve-many", func(w http.ResponseWriter, r *http.Request) { + d := r.URL.Query().Get("domain") + if d == "" { + d = domain + } + n := 20 + if ns := r.URL.Query().Get("n"); ns != "" { + if parsed, err := strconv.Atoi(ns); err == nil && parsed > 0 { + n = parsed + } + } + + seen := make(map[string]bool) + type result struct { + Iteration int `json:"iteration"` + IPs []string `json:"ips,omitempty"` + New bool `json:"new"` + Error string `json:"error,omitempty"` + } + results := make([]result, 0, n) + + for i := 1; i <= n; i++ { + ips, err := net.LookupHost(d) + if err != nil { + results = append(results, result{Iteration: i, Error: err.Error()}) + } else { + key := fmt.Sprintf("%v", ips) + isNew := !seen[key] + seen[key] = true + results = append(results, result{Iteration: i, IPs: ips, New: isNew}) + } + time.Sleep(50 * time.Millisecond) + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]interface{}{ + "domain": d, + "total_queries": n, + "unique_ip_sets": len(seen), + "results": results, + }) + }) + + port := "8086" + fmt.Printf("DNS dedup test server starting on :%s\n", port) + if err := http.ListenAndServe(":"+port, nil); err != nil { + fmt.Fprintf(os.Stderr, "server error: %v\n", err) + os.Exit(1) + } +}