updateTemplateValuesFromHTTPResp checks the HTTP response body and the previous templatized response body and updates the template values that are currently held under templateValuesMu.
(logger *zap.Logger, templatedResponse, resp models.HTTPResp, currentTemplatedValues, prevTemplatedValues map[string]interface{})
| 2001 | // templatized response body and updates the template values that are currently held |
| 2002 | // under templateValuesMu. |
| 2003 | func updateTemplateValuesFromHTTPResp(logger *zap.Logger, templatedResponse, resp models.HTTPResp, currentTemplatedValues, prevTemplatedValues map[string]interface{}) bool { |
| 2004 | // We derive template keys directly from the templated response body & headers by |
| 2005 | // scanning for placeholder patterns like {{key}} (go text/template simple identifiers) |
| 2006 | // and then recursively locating the same JSON path in the new response to fetch |
| 2007 | // the concrete value. This avoids relying on updateTemplatesFromJSON and gives |
| 2008 | // deterministic path-based updates. |
| 2009 | if len(currentTemplatedValues) == 0 { // nothing to update |
| 2010 | logger.Debug("no templatized values present, nothing to update") |
| 2011 | return false |
| 2012 | } |
| 2013 | |
| 2014 | // Capture entire inner expression (supports: {{string .token}}, {{ .id }}, {{token}}, {{ float .price | printf "%f" }}) |
| 2015 | placeholderRe := regexp.MustCompile(`{{\s*([^{}]+?)\s*}}`) |
| 2016 | changed := false |
| 2017 | |
| 2018 | // --- 1. Handle JSON body path-based updates --- |
| 2019 | // Problem: templated response can contain raw placeholders (e.g. array: [{{int .x}},{{int .y}}]) which is not valid JSON. |
| 2020 | // Solution: produce a sanitized JSON by wrapping any unquoted placeholder token in quotes so that the body becomes parseable. |
| 2021 | var templatedParsed interface{} |
| 2022 | var actualParsed interface{} |
| 2023 | sanitizedTemplatedBody := sanitizeTemplatedJSON(templatedResponse.Body, placeholderRe) |
| 2024 | templatedIsJSON := json.Valid([]byte(sanitizedTemplatedBody)) |
| 2025 | actualIsJSON := json.Valid([]byte(resp.Body)) |
| 2026 | |
| 2027 | if templatedIsJSON && actualIsJSON { |
| 2028 | if err := json.Unmarshal([]byte(sanitizedTemplatedBody), &templatedParsed); err == nil { |
| 2029 | if err2 := json.Unmarshal([]byte(resp.Body), &actualParsed); err2 == nil { |
| 2030 | if traverseAndUpdateTemplates(logger, templatedParsed, actualParsed, "", placeholderRe, currentTemplatedValues, prevTemplatedValues) { |
| 2031 | changed = true |
| 2032 | } |
| 2033 | } |
| 2034 | } |
| 2035 | } else { |
| 2036 | logger.Debug("response body or templated body is not JSON, skipping body path-based template updates", zap.Bool("templatedIsJSON", templatedIsJSON), zap.Bool("actualIsJSON", actualIsJSON)) |
| 2037 | } |
| 2038 | |
| 2039 | // --- 2. Handle response header updates --- |
| 2040 | // The record-side (pkg/service/tools/templatize.go buildValueIndexV2) can rewrite |
| 2041 | // header values (including Set-Cookie sub-components and "Authorization: Bearer <tok>") |
| 2042 | // into {{.VarName}} placeholders. At replay time the live server sends fresh values |
| 2043 | // for those same headers — we must pull them out so downstream test cases that |
| 2044 | // consume the placeholder (e.g. Cookie: session={{.session}}) render the live value. |
| 2045 | if updateTemplateValuesFromHeaders(logger, templatedResponse.Header, resp.Header, placeholderRe, currentTemplatedValues, prevTemplatedValues) { |
| 2046 | changed = true |
| 2047 | } |
| 2048 | return changed |
| 2049 | } |
| 2050 | |
| 2051 | // updateTemplateValuesFromHeaders walks the recorded (templated) response headers side-by-side |
| 2052 | // with the live response headers. For each recorded header value that carries a {{.Var}} |