173 lines
80 KiB
HTML
173 lines
80 KiB
HTML
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Home Assistant MCP Server Documentation"><link rel=canonical href=https://jango-blockchained.github.io/advanced-homeassistant-mcp/testing.html><link rel=prev href=contributing.html><link rel=next href=development/best-practices.html><link rel=icon href=assets/images/favicon.ico><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.6.2"><title>Testing - MCP Server for Home Assistant</title><link rel=stylesheet href=assets/stylesheets/main.d7758b05.min.css><link rel=stylesheet href=assets/stylesheets/palette.06af60db.min.css><script src=https://unpkg.com/iframe-worker/shim></script><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"><style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style><link rel=stylesheet href=assets/_mkdocstrings.css><link rel=stylesheet href=stylesheets/extra.css><script>__md_scope=new URL(".",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script><script id=__analytics>function __md_analytics(){function e(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],e("js",new Date),e("config",""),document.addEventListener("DOMContentLoaded",(function(){document.forms.search&&document.forms.search.query.addEventListener("blur",(function(){this.value&&e("event","search",{search_term:this.value})}));document$.subscribe((function(){var t=document.forms.feedback;if(void 0!==t)for(var a of t.querySelectorAll("[type=submit]"))a.addEventListener("click",(function(a){a.preventDefault();var n=document.location.pathname,d=this.getAttribute("data-md-value");e("event","feedback",{page:n,data:d}),t.firstElementChild.disabled=!0;var r=t.querySelector(".md-feedback__note [data-md-value='"+d+"']");r&&(r.hidden=!1)})),t.hidden=!1})),location$.subscribe((function(t){e("config","",{page_path:t.pathname})}))}));var t=document.createElement("script");t.async=!0,t.src="https://www.googletagmanager.com/gtag/js?id=",document.getElementById("__analytics").insertAdjacentElement("afterEnd",t)}</script><script>if("undefined"!=typeof __md_analytics){var consent=__md_get("__consent");consent&&consent.analytics&&__md_analytics()}</script></head> <body dir=ltr data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#testing-documentation class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class="md-header md-header--shadow md-header--lifted" data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=index.html title="MCP Server for Home Assistant" class="md-header__button md-logo" aria-label="MCP Server for Home Assistant" data-md-component=logo> <img src=assets/images/logo.png alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> MCP Server for Home Assistant </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Testing </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Switch to light mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5s-1.65.15-2.39.42zM3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29zm.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14zM20.65 7l-1.77 3.79a7.02 7.02 0 0 0-2.38-4.15zm-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29zM12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=deep-purple data-md-color-accent=purple aria-label="Switch to dark mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3zm3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95zm-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg> </label> <div class=md-search data-md-component=search role=dialog> <label class=md-search__overlay for=__search></label> <div class=md-search__inner role=search> <form class=md-search__form name=search> <input type=text class=md-search__input name=query aria-label=Search placeholder=Search autocapitalize=off autocorrect=off autocomplete=off spellcheck=false data-md-component=search-query required> <label class="md-search__icon md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg> </label> <nav class=md-search__options aria-label=Search> <a href=javascript:void(0) class="md-search__icon md-icon" title=Share aria-label=Share data-clipboard data-clipboard-text data-md-component=search-share tabindex=-1> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg> </a> <button type=reset class="md-search__icon md-icon" title=Clear aria-label=Clear tabindex=-1> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg> </button> </nav> <div class=md-search__suggest data-md-component=search-suggest></div> </form> <div class=md-search__output> <div class=md-search__scrollwrap tabindex=0 data-md-scrollfix> <div class=md-search-result data-md-component=search-result> <div class=md-search-result__meta> Initializing search </div> <ol class=md-search-result__list role=presentation></ol> </div> </div> </div> </div> </div> <div class=md-header__source> <a href=https://github.com/jango-blockchained/advanced-homeassistant-mcp title="Go to repository" class=md-source data-md-component=source> <div class="md-source__icon md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 496 512"><!-- Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </div> <div class=md-source__repository> GitHub </div> </a> </div> </nav> <nav class=md-tabs aria-label=Tabs data-md-component=tabs> <div class=md-grid> <ul class=md-tabs__list> <li class=md-tabs__item> <a href=index.html class=md-tabs__link> Home </a> </li> <li class=md-tabs__item> <a href=getting-started/index.html class=md-tabs__link> Getting Started </a> </li> <li class=md-tabs__item> <a href=api/index.html class=md-tabs__link> API Reference </a> </li> <li class=md-tabs__item> <a href=usage.html class=md-tabs__link> Usage </a> </li> <li class=md-tabs__item> <a href=config/index.html class=md-tabs__link> Configuration </a> </li> <li class=md-tabs__item> <a href=tools/index.html class=md-tabs__link> Tools </a> </li> <li class="md-tabs__item md-tabs__item--active"> <a href=development/index.html class=md-tabs__link> Development </a> </li> <li class=md-tabs__item> <a href=examples/index.html class=md-tabs__link> Examples </a> </li> </ul> </div> </nav> </header> <div class=md-container data-md-component=container> <main class=md-main data-md-component=main> <div class="md-main__inner md-grid"> <div class="md-sidebar md-sidebar--primary" data-md-component=sidebar data-md-type=navigation> <div class=md-sidebar__scrollwrap> <div class=md-sidebar__inner> <nav class="md-nav md-nav--primary md-nav--lifted md-nav--integrated" aria-label=Navigation data-md-level=0> <label class=md-nav__title for=__drawer> <a href=index.html title="MCP Server for Home Assistant" class="md-nav__button md-logo" aria-label="MCP Server for Home Assistant" data-md-component=logo> <img src=assets/images/logo.png alt=logo> </a> MCP Server for Home Assistant </label> <div class=md-nav__source> <a href=https://github.com/jango-blockchained/advanced-homeassistant-mcp title="Go to repository" class=md-source data-md-component=source> <div class="md-source__icon md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 496 512"><!-- Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </div> <div class=md-source__repository> GitHub </div> </a> </div> <ul class=md-nav__list data-md-scrollfix> <li class=md-nav__item> <a href=index.html class=md-nav__link> <span class=md-ellipsis> Home </span> </a> </li> <li class="md-nav__item md-nav__item--pruned md-nav__item--nested"> <a href=getting-started/index.html class=md-nav__link> <span class=md-ellipsis> Getting Started </span> <span class="md-nav__icon md-icon"></span> </a> </li> <li class="md-nav__item md-nav__item--pruned md-nav__item--nested"> <a href=api/index.html class=md-nav__link> <span class=md-ellipsis> API Reference </span> <span class="md-nav__icon md-icon"></span> </a> </li> <li class=md-nav__item> <a href=usage.html class=md-nav__link> <span class=md-ellipsis> Usage </span> </a> </li> <li class="md-nav__item md-nav__item--pruned md-nav__item--nested"> <a href=config/index.html class=md-nav__link> <span class=md-ellipsis> Configuration </span> <span class="md-nav__icon md-icon"></span> </a> </li> <li class="md-nav__item md-nav__item--pruned md-nav__item--nested"> <a href=tools/index.html class=md-nav__link> <span class=md-ellipsis> Tools </span> <span class="md-nav__icon md-icon"></span> </a> </li> <li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type=checkbox id=__nav_7 checked> <div class="md-nav__link md-nav__container"> <a href=development/index.html class="md-nav__link "> <span class=md-ellipsis> Development </span> </a> <label class="md-nav__link " for=__nav_7 id=__nav_7_label tabindex> <span class="md-nav__icon md-icon"></span> </label> </div> <nav class=md-nav data-md-level=1 aria-labelledby=__nav_7_label aria-expanded=true> <label class=md-nav__title for=__nav_7> <span class="md-nav__icon md-icon"></span> Development </label> <ul class=md-nav__list data-md-scrollfix> <li class=md-nav__item> <a href=development/environment.html class=md-nav__link> <span class=md-ellipsis> Environment Setup </span> </a> </li> <li class=md-nav__item> <a href=architecture.html class=md-nav__link> <span class=md-ellipsis> Architecture </span> </a> </li> <li class=md-nav__item> <a href=contributing.html class=md-nav__link> <span class=md-ellipsis> Contributing </span> </a> </li> <li class="md-nav__item md-nav__item--active"> <input class="md-nav__toggle md-toggle" type=checkbox id=__toc> <label class="md-nav__link md-nav__link--active" for=__toc> <span class=md-ellipsis> Testing </span> <span class="md-nav__icon md-icon"></span> </label> <a href=testing.html class="md-nav__link md-nav__link--active"> <span class=md-ellipsis> Testing </span> </a> <nav class="md-nav md-nav--secondary" aria-label="Table of contents"> <label class=md-nav__title for=__toc> <span class="md-nav__icon md-icon"></span> Table of contents </label> <ul class=md-nav__list data-md-component=toc data-md-scrollfix> <li class=md-nav__item> <a href=#quick-reference class=md-nav__link> <span class=md-ellipsis> Quick Reference </span> </a> </li> <li class=md-nav__item> <a href=#overview class=md-nav__link> <span class=md-ellipsis> Overview </span> </a> </li> <li class=md-nav__item> <a href=#test-structure class=md-nav__link> <span class=md-ellipsis> Test Structure </span> </a> </li> <li class=md-nav__item> <a href=#test-configuration class=md-nav__link> <span class=md-ellipsis> Test Configuration </span> </a> <nav class=md-nav aria-label="Test Configuration"> <ul class=md-nav__list> <li class=md-nav__item> <a href=#bun-test-configuration-bunfigtoml class=md-nav__link> <span class=md-ellipsis> Bun Test Configuration (bunfig.toml) </span> </a> </li> <li class=md-nav__item> <a href=#bun-scripts class=md-nav__link> <span class=md-ellipsis> Bun Scripts </span> </a> </li> </ul> </nav> </li> <li class=md-nav__item> <a href=#test-setup class=md-nav__link> <span class=md-ellipsis> Test Setup </span> </a> <nav class=md-nav aria-label="Test Setup"> <ul class=md-nav__list> <li class=md-nav__item> <a href=#global-configuration class=md-nav__link> <span class=md-ellipsis> Global Configuration </span> </a> </li> <li class=md-nav__item> <a href=#test-environment class=md-nav__link> <span class=md-ellipsis> Test Environment </span> </a> </li> </ul> </nav> </li> <li class=md-nav__item> <a href=#running-tests class=md-nav__link> <span class=md-ellipsis> Running Tests </span> </a> </li> <li class=md-nav__item> <a href=#advanced-debugging class=md-nav__link> <span class=md-ellipsis> Advanced Debugging </span> </a> <nav class=md-nav aria-label="Advanced Debugging"> <ul class=md-nav__list> <li class=md-nav__item> <a href=#using-node-inspector class=md-nav__link> <span class=md-ellipsis> Using Node Inspector </span> </a> </li> <li class=md-nav__item> <a href=#using-vs-code class=md-nav__link> <span class=md-ellipsis> Using VS Code </span> </a> </li> <li class=md-nav__item> <a href=#test-isolation class=md-nav__link> <span class=md-ellipsis> Test Isolation </span> </a> </li> </ul> </nav> </li> <li class=md-nav__item> <a href=#writing-tests class=md-nav__link> <span class=md-ellipsis> Writing Tests </span> </a> <nav class=md-nav aria-label="Writing Tests"> <ul class=md-nav__list> <li class=md-nav__item> <a href=#test-file-naming class=md-nav__link> <span class=md-ellipsis> Test File Naming </span> </a> </li> <li class=md-nav__item> <a href=#example-test-structure class=md-nav__link> <span class=md-ellipsis> Example Test Structure </span> </a> </li> </ul> </nav> </li> <li class=md-nav__item> <a href=#coverage class=md-nav__link> <span class=md-ellipsis> Coverage </span> </a> </li> <li class=md-nav__item> <a href=#security-middleware-testing class=md-nav__link> <span class=md-ellipsis> Security Middleware Testing </span> </a> <nav class=md-nav aria-label="Security Middleware Testing"> <ul class=md-nav__list> <li class=md-nav__item> <a href=#utility-function-testing class=md-nav__link> <span class=md-ellipsis> Utility Function Testing </span> </a> <nav class=md-nav aria-label="Utility Function Testing"> <ul class=md-nav__list> <li class=md-nav__item> <a href=#key-utility-functions class=md-nav__link> <span class=md-ellipsis> Key Utility Functions </span> </a> </li> </ul> </nav> </li> <li class=md-nav__item> <a href=#testing-philosophy class=md-nav__link> <span class=md-ellipsis> Testing Philosophy </span> </a> </li> <li class=md-nav__item> <a href=#best-practices class=md-nav__link> <span class=md-ellipsis> Best Practices </span> </a> </li> <li class=md-nav__item> <a href=#running-security-tests class=md-nav__link> <span class=md-ellipsis> Running Security Tests </span> </a> </li> <li class=md-nav__item> <a href=#continuous-improvement class=md-nav__link> <span class=md-ellipsis> Continuous Improvement </span> </a> </li> </ul> </nav> </li> <li class=md-nav__item> <a href=#best-practices_1 class=md-nav__link> <span class=md-ellipsis> Best Practices </span> </a> </li> <li class=md-nav__item> <a href=#coverage_1 class=md-nav__link> <span class=md-ellipsis> Coverage </span> </a> </li> <li class=md-nav__item> <a href=#debugging-tests class=md-nav__link> <span class=md-ellipsis> Debugging Tests </span> </a> <nav class=md-nav aria-label="Debugging Tests"> <ul class=md-nav__list> <li class=md-nav__item> <a href=#advanced-debugging_1 class=md-nav__link> <span class=md-ellipsis> Advanced Debugging </span> </a> </li> </ul> </nav> </li> <li class=md-nav__item> <a href=#contributing class=md-nav__link> <span class=md-ellipsis> Contributing </span> </a> </li> <li class=md-nav__item> <a href=#coverage-requirements class=md-nav__link> <span class=md-ellipsis> Coverage Requirements </span> </a> </li> </ul> </nav> </li> <li class=md-nav__item> <a href=development/best-practices.html class=md-nav__link> <span class=md-ellipsis> Best Practices </span> </a> </li> <li class=md-nav__item> <a href=development/interfaces.html class=md-nav__link> <span class=md-ellipsis> Interfaces </span> </a> </li> <li class=md-nav__item> <a href=development/tools.html class=md-nav__link> <span class=md-ellipsis> Tool Development </span> </a> </li> <li class=md-nav__item> <a href=development/test-migration-guide.html class=md-nav__link> <span class=md-ellipsis> Test Migration Guide </span> </a> </li> <li class=md-nav__item> <a href=troubleshooting.html class=md-nav__link> <span class=md-ellipsis> Troubleshooting </span> </a> </li> <li class=md-nav__item> <a href=deployment.html class=md-nav__link> <span class=md-ellipsis> Deployment </span> </a> </li> <li class=md-nav__item> <a href=roadmap.html class=md-nav__link> <span class=md-ellipsis> Roadmap </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--pruned md-nav__item--nested"> <a href=examples/index.html class=md-nav__link> <span class=md-ellipsis> Examples </span> <span class="md-nav__icon md-icon"></span> </a> </li> </ul> </nav> </div> </div> </div> <div class=md-content data-md-component=content> <article class="md-content__inner md-typeset"> <h1 id=testing-documentation>Testing Documentation</h1> <h2 id=quick-reference>Quick Reference</h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-0-1><a id=__codelineno-0-1 name=__codelineno-0-1 href=#__codelineno-0-1></a><span class=c1># Most Common Commands</span>
|
|
</span><span id=__span-0-2><a id=__codelineno-0-2 name=__codelineno-0-2 href=#__codelineno-0-2></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span><span class=c1># Run all tests</span>
|
|
</span><span id=__span-0-3><a id=__codelineno-0-3 name=__codelineno-0-3 href=#__codelineno-0-3></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--watch<span class=w> </span><span class=c1># Run tests in watch mode</span>
|
|
</span><span id=__span-0-4><a id=__codelineno-0-4 name=__codelineno-0-4 href=#__codelineno-0-4></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--coverage<span class=w> </span><span class=c1># Run tests with coverage</span>
|
|
</span><span id=__span-0-5><a id=__codelineno-0-5 name=__codelineno-0-5 href=#__codelineno-0-5></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>path/to/test.ts<span class=w> </span><span class=c1># Run a specific test file</span>
|
|
</span><span id=__span-0-6><a id=__codelineno-0-6 name=__codelineno-0-6 href=#__codelineno-0-6></a>
|
|
</span><span id=__span-0-7><a id=__codelineno-0-7 name=__codelineno-0-7 href=#__codelineno-0-7></a><span class=c1># Additional Options</span>
|
|
</span><span id=__span-0-8><a id=__codelineno-0-8 name=__codelineno-0-8 href=#__codelineno-0-8></a><span class=nv>DEBUG</span><span class=o>=</span><span class=nb>true</span><span class=w> </span>bun<span class=w> </span><span class=nb>test</span><span class=w> </span><span class=c1># Run with debug output</span>
|
|
</span><span id=__span-0-9><a id=__codelineno-0-9 name=__codelineno-0-9 href=#__codelineno-0-9></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--pattern<span class=w> </span><span class=s2>"auth"</span><span class=w> </span><span class=c1># Run tests matching a pattern</span>
|
|
</span><span id=__span-0-10><a id=__codelineno-0-10 name=__codelineno-0-10 href=#__codelineno-0-10></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--timeout<span class=w> </span><span class=m>60000</span><span class=w> </span><span class=c1># Run with a custom timeout</span>
|
|
</span></code></pre></div> <h2 id=overview>Overview</h2> <p>This document describes the testing setup and practices used in the Home Assistant MCP project. We use Bun's test runner for both unit and integration testing, ensuring comprehensive coverage across modules.</p> <h2 id=test-structure>Test Structure</h2> <p>Tests are organized in two main locations:</p> <ol> <li><strong>Root Level Integration Tests</strong> (<code>/__tests__/</code>):</li> </ol> <div class="language-text highlight"><pre><span></span><code><span id=__span-1-1><a id=__codelineno-1-1 name=__codelineno-1-1 href=#__codelineno-1-1></a>__tests__/
|
|
</span><span id=__span-1-2><a id=__codelineno-1-2 name=__codelineno-1-2 href=#__codelineno-1-2></a>├── ai/ # AI/ML component tests
|
|
</span><span id=__span-1-3><a id=__codelineno-1-3 name=__codelineno-1-3 href=#__codelineno-1-3></a>├── api/ # API integration tests
|
|
</span><span id=__span-1-4><a id=__codelineno-1-4 name=__codelineno-1-4 href=#__codelineno-1-4></a>├── context/ # Context management tests
|
|
</span><span id=__span-1-5><a id=__codelineno-1-5 name=__codelineno-1-5 href=#__codelineno-1-5></a>├── hass/ # Home Assistant integration tests
|
|
</span><span id=__span-1-6><a id=__codelineno-1-6 name=__codelineno-1-6 href=#__codelineno-1-6></a>├── schemas/ # Schema validation tests
|
|
</span><span id=__span-1-7><a id=__codelineno-1-7 name=__codelineno-1-7 href=#__codelineno-1-7></a>├── security/ # Security integration tests
|
|
</span><span id=__span-1-8><a id=__codelineno-1-8 name=__codelineno-1-8 href=#__codelineno-1-8></a>├── tools/ # Tools and utilities tests
|
|
</span><span id=__span-1-9><a id=__codelineno-1-9 name=__codelineno-1-9 href=#__codelineno-1-9></a>├── websocket/ # WebSocket integration tests
|
|
</span><span id=__span-1-10><a id=__codelineno-1-10 name=__codelineno-1-10 href=#__codelineno-1-10></a>├── helpers.test.ts # Helper function tests
|
|
</span><span id=__span-1-11><a id=__codelineno-1-11 name=__codelineno-1-11 href=#__codelineno-1-11></a>├── index.test.ts # Main application tests
|
|
</span><span id=__span-1-12><a id=__codelineno-1-12 name=__codelineno-1-12 href=#__codelineno-1-12></a>└── server.test.ts # Server integration tests
|
|
</span></code></pre></div> <ol> <li><strong>Component Level Unit Tests</strong> (<code>src/**/</code>):</li> </ol> <div class="language-text highlight"><pre><span></span><code><span id=__span-2-1><a id=__codelineno-2-1 name=__codelineno-2-1 href=#__codelineno-2-1></a>src/
|
|
</span><span id=__span-2-2><a id=__codelineno-2-2 name=__codelineno-2-2 href=#__codelineno-2-2></a>├── __tests__/ # Global test setup and utilities
|
|
</span><span id=__span-2-3><a id=__codelineno-2-3 name=__codelineno-2-3 href=#__codelineno-2-3></a>│ └── setup.ts # Global test configuration
|
|
</span><span id=__span-2-4><a id=__codelineno-2-4 name=__codelineno-2-4 href=#__codelineno-2-4></a>├── component/
|
|
</span><span id=__span-2-5><a id=__codelineno-2-5 name=__codelineno-2-5 href=#__codelineno-2-5></a>│ ├── __tests__/ # Component-specific unit tests
|
|
</span><span id=__span-2-6><a id=__codelineno-2-6 name=__codelineno-2-6 href=#__codelineno-2-6></a>│ └── component.ts
|
|
</span></code></pre></div> <h2 id=test-configuration>Test Configuration</h2> <h3 id=bun-test-configuration-bunfigtoml>Bun Test Configuration (<code>bunfig.toml</code>)</h3> <div class="language-toml highlight"><pre><span></span><code><span id=__span-3-1><a id=__codelineno-3-1 name=__codelineno-3-1 href=#__codelineno-3-1></a><span class=k>[test]</span>
|
|
</span><span id=__span-3-2><a id=__codelineno-3-2 name=__codelineno-3-2 href=#__codelineno-3-2></a><span class=n>preload</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=p>[</span><span class=s2>"./src/__tests__/setup.ts"</span><span class=p>]</span><span class=w> </span><span class=c1># Global test setup</span>
|
|
</span><span id=__span-3-3><a id=__codelineno-3-3 name=__codelineno-3-3 href=#__codelineno-3-3></a><span class=n>coverage</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=kc>true</span><span class=w> </span><span class=c1># Enable coverage by default</span>
|
|
</span><span id=__span-3-4><a id=__codelineno-3-4 name=__codelineno-3-4 href=#__codelineno-3-4></a><span class=n>timeout</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=mi>30000</span><span class=w> </span><span class=c1># Test timeout in milliseconds</span>
|
|
</span><span id=__span-3-5><a id=__codelineno-3-5 name=__codelineno-3-5 href=#__codelineno-3-5></a><span class=n>testMatch</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=p>[</span><span class=s2>"**/__tests__/**/*.test.ts"</span><span class=p>]</span><span class=w> </span><span class=c1># Test file patterns</span>
|
|
</span></code></pre></div> <h3 id=bun-scripts>Bun Scripts</h3> <p>Available test commands in <code>package.json</code>:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-4-1><a id=__codelineno-4-1 name=__codelineno-4-1 href=#__codelineno-4-1></a><span class=c1># Run all tests</span>
|
|
</span><span id=__span-4-2><a id=__codelineno-4-2 name=__codelineno-4-2 href=#__codelineno-4-2></a>bun<span class=w> </span><span class=nb>test</span>
|
|
</span><span id=__span-4-3><a id=__codelineno-4-3 name=__codelineno-4-3 href=#__codelineno-4-3></a>
|
|
</span><span id=__span-4-4><a id=__codelineno-4-4 name=__codelineno-4-4 href=#__codelineno-4-4></a><span class=c1># Watch mode for development</span>
|
|
</span><span id=__span-4-5><a id=__codelineno-4-5 name=__codelineno-4-5 href=#__codelineno-4-5></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--watch
|
|
</span><span id=__span-4-6><a id=__codelineno-4-6 name=__codelineno-4-6 href=#__codelineno-4-6></a>
|
|
</span><span id=__span-4-7><a id=__codelineno-4-7 name=__codelineno-4-7 href=#__codelineno-4-7></a><span class=c1># Generate coverage report</span>
|
|
</span><span id=__span-4-8><a id=__codelineno-4-8 name=__codelineno-4-8 href=#__codelineno-4-8></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--coverage
|
|
</span><span id=__span-4-9><a id=__codelineno-4-9 name=__codelineno-4-9 href=#__codelineno-4-9></a>
|
|
</span><span id=__span-4-10><a id=__codelineno-4-10 name=__codelineno-4-10 href=#__codelineno-4-10></a><span class=c1># Run linting</span>
|
|
</span><span id=__span-4-11><a id=__codelineno-4-11 name=__codelineno-4-11 href=#__codelineno-4-11></a>bun<span class=w> </span>run<span class=w> </span>lint
|
|
</span><span id=__span-4-12><a id=__codelineno-4-12 name=__codelineno-4-12 href=#__codelineno-4-12></a>
|
|
</span><span id=__span-4-13><a id=__codelineno-4-13 name=__codelineno-4-13 href=#__codelineno-4-13></a><span class=c1># Format code</span>
|
|
</span><span id=__span-4-14><a id=__codelineno-4-14 name=__codelineno-4-14 href=#__codelineno-4-14></a>bun<span class=w> </span>run<span class=w> </span>format
|
|
</span></code></pre></div> <h2 id=test-setup>Test Setup</h2> <h3 id=global-configuration>Global Configuration</h3> <p>A global test setup file (<code>src/__tests__/setup.ts</code>) provides: - Environment configuration - Mock utilities - Test helper functions - Global lifecycle hooks</p> <h3 id=test-environment>Test Environment</h3> <ul> <li>Environment variables are loaded from <code>.env.test</code>.</li> <li>Console output is minimized unless <code>DEBUG=true</code>.</li> <li>JWT secrets and tokens are preconfigured for testing.</li> <li>Rate limiting and security features are initialized appropriately.</li> </ul> <h2 id=running-tests>Running Tests</h2> <div class="language-bash highlight"><pre><span></span><code><span id=__span-5-1><a id=__codelineno-5-1 name=__codelineno-5-1 href=#__codelineno-5-1></a><span class=c1># Basic test run</span>
|
|
</span><span id=__span-5-2><a id=__codelineno-5-2 name=__codelineno-5-2 href=#__codelineno-5-2></a>bun<span class=w> </span><span class=nb>test</span>
|
|
</span><span id=__span-5-3><a id=__codelineno-5-3 name=__codelineno-5-3 href=#__codelineno-5-3></a>
|
|
</span><span id=__span-5-4><a id=__codelineno-5-4 name=__codelineno-5-4 href=#__codelineno-5-4></a><span class=c1># Run tests with coverage</span>
|
|
</span><span id=__span-5-5><a id=__codelineno-5-5 name=__codelineno-5-5 href=#__codelineno-5-5></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--coverage
|
|
</span><span id=__span-5-6><a id=__codelineno-5-6 name=__codelineno-5-6 href=#__codelineno-5-6></a>
|
|
</span><span id=__span-5-7><a id=__codelineno-5-7 name=__codelineno-5-7 href=#__codelineno-5-7></a><span class=c1># Run a specific test file</span>
|
|
</span><span id=__span-5-8><a id=__codelineno-5-8 name=__codelineno-5-8 href=#__codelineno-5-8></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>path/to/test.test.ts
|
|
</span><span id=__span-5-9><a id=__codelineno-5-9 name=__codelineno-5-9 href=#__codelineno-5-9></a>
|
|
</span><span id=__span-5-10><a id=__codelineno-5-10 name=__codelineno-5-10 href=#__codelineno-5-10></a><span class=c1># Run tests in watch mode</span>
|
|
</span><span id=__span-5-11><a id=__codelineno-5-11 name=__codelineno-5-11 href=#__codelineno-5-11></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--watch
|
|
</span><span id=__span-5-12><a id=__codelineno-5-12 name=__codelineno-5-12 href=#__codelineno-5-12></a>
|
|
</span><span id=__span-5-13><a id=__codelineno-5-13 name=__codelineno-5-13 href=#__codelineno-5-13></a><span class=c1># Run tests with debug output</span>
|
|
</span><span id=__span-5-14><a id=__codelineno-5-14 name=__codelineno-5-14 href=#__codelineno-5-14></a><span class=nv>DEBUG</span><span class=o>=</span><span class=nb>true</span><span class=w> </span>bun<span class=w> </span><span class=nb>test</span>
|
|
</span><span id=__span-5-15><a id=__codelineno-5-15 name=__codelineno-5-15 href=#__codelineno-5-15></a>
|
|
</span><span id=__span-5-16><a id=__codelineno-5-16 name=__codelineno-5-16 href=#__codelineno-5-16></a><span class=c1># Run tests with increased timeout</span>
|
|
</span><span id=__span-5-17><a id=__codelineno-5-17 name=__codelineno-5-17 href=#__codelineno-5-17></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--timeout<span class=w> </span><span class=m>60000</span>
|
|
</span><span id=__span-5-18><a id=__codelineno-5-18 name=__codelineno-5-18 href=#__codelineno-5-18></a>
|
|
</span><span id=__span-5-19><a id=__codelineno-5-19 name=__codelineno-5-19 href=#__codelineno-5-19></a><span class=c1># Run tests matching a pattern</span>
|
|
</span><span id=__span-5-20><a id=__codelineno-5-20 name=__codelineno-5-20 href=#__codelineno-5-20></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--pattern<span class=w> </span><span class=s2>"auth"</span>
|
|
</span></code></pre></div> <h2 id=advanced-debugging>Advanced Debugging</h2> <h3 id=using-node-inspector>Using Node Inspector</h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-6-1><a id=__codelineno-6-1 name=__codelineno-6-1 href=#__codelineno-6-1></a><span class=c1># Start tests with inspector</span>
|
|
</span><span id=__span-6-2><a id=__codelineno-6-2 name=__codelineno-6-2 href=#__codelineno-6-2></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--inspect
|
|
</span><span id=__span-6-3><a id=__codelineno-6-3 name=__codelineno-6-3 href=#__codelineno-6-3></a>
|
|
</span><span id=__span-6-4><a id=__codelineno-6-4 name=__codelineno-6-4 href=#__codelineno-6-4></a><span class=c1># Start tests with inspector and break on first line</span>
|
|
</span><span id=__span-6-5><a id=__codelineno-6-5 name=__codelineno-6-5 href=#__codelineno-6-5></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--inspect-brk
|
|
</span></code></pre></div> <h3 id=using-vs-code>Using VS Code</h3> <p>Create a launch configuration in <code>.vscode/launch.json</code>:</p> <div class="language-json highlight"><pre><span></span><code><span id=__span-7-1><a id=__codelineno-7-1 name=__codelineno-7-1 href=#__codelineno-7-1></a><span class=p>{</span>
|
|
</span><span id=__span-7-2><a id=__codelineno-7-2 name=__codelineno-7-2 href=#__codelineno-7-2></a><span class=w> </span><span class=nt>"version"</span><span class=p>:</span><span class=w> </span><span class=s2>"0.2.0"</span><span class=p>,</span>
|
|
</span><span id=__span-7-3><a id=__codelineno-7-3 name=__codelineno-7-3 href=#__codelineno-7-3></a><span class=w> </span><span class=nt>"configurations"</span><span class=p>:</span><span class=w> </span><span class=p>[</span>
|
|
</span><span id=__span-7-4><a id=__codelineno-7-4 name=__codelineno-7-4 href=#__codelineno-7-4></a><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-7-5><a id=__codelineno-7-5 name=__codelineno-7-5 href=#__codelineno-7-5></a><span class=w> </span><span class=nt>"type"</span><span class=p>:</span><span class=w> </span><span class=s2>"bun"</span><span class=p>,</span>
|
|
</span><span id=__span-7-6><a id=__codelineno-7-6 name=__codelineno-7-6 href=#__codelineno-7-6></a><span class=w> </span><span class=nt>"request"</span><span class=p>:</span><span class=w> </span><span class=s2>"launch"</span><span class=p>,</span>
|
|
</span><span id=__span-7-7><a id=__codelineno-7-7 name=__codelineno-7-7 href=#__codelineno-7-7></a><span class=w> </span><span class=nt>"name"</span><span class=p>:</span><span class=w> </span><span class=s2>"Debug Tests"</span><span class=p>,</span>
|
|
</span><span id=__span-7-8><a id=__codelineno-7-8 name=__codelineno-7-8 href=#__codelineno-7-8></a><span class=w> </span><span class=nt>"program"</span><span class=p>:</span><span class=w> </span><span class=s2>"${workspaceFolder}/node_modules/bun/bin/bun"</span><span class=p>,</span>
|
|
</span><span id=__span-7-9><a id=__codelineno-7-9 name=__codelineno-7-9 href=#__codelineno-7-9></a><span class=w> </span><span class=nt>"args"</span><span class=p>:</span><span class=w> </span><span class=p>[</span><span class=s2>"test"</span><span class=p>,</span><span class=w> </span><span class=s2>"${file}"</span><span class=p>],</span>
|
|
</span><span id=__span-7-10><a id=__codelineno-7-10 name=__codelineno-7-10 href=#__codelineno-7-10></a><span class=w> </span><span class=nt>"cwd"</span><span class=p>:</span><span class=w> </span><span class=s2>"${workspaceFolder}"</span><span class=p>,</span>
|
|
</span><span id=__span-7-11><a id=__codelineno-7-11 name=__codelineno-7-11 href=#__codelineno-7-11></a><span class=w> </span><span class=nt>"env"</span><span class=p>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nt>"DEBUG"</span><span class=p>:</span><span class=w> </span><span class=s2>"true"</span><span class=w> </span><span class=p>}</span>
|
|
</span><span id=__span-7-12><a id=__codelineno-7-12 name=__codelineno-7-12 href=#__codelineno-7-12></a><span class=w> </span><span class=p>}</span>
|
|
</span><span id=__span-7-13><a id=__codelineno-7-13 name=__codelineno-7-13 href=#__codelineno-7-13></a><span class=w> </span><span class=p>]</span>
|
|
</span><span id=__span-7-14><a id=__codelineno-7-14 name=__codelineno-7-14 href=#__codelineno-7-14></a><span class=p>}</span>
|
|
</span></code></pre></div> <h3 id=test-isolation>Test Isolation</h3> <p>To run a single test in isolation:</p> <div class="language-typescript highlight"><pre><span></span><code><span id=__span-8-1><a id=__codelineno-8-1 name=__codelineno-8-1 href=#__codelineno-8-1></a><span class=nx>describe</span><span class=p>.</span><span class=nx>only</span><span class=p>(</span><span class=s2>"specific test suite"</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-8-2><a id=__codelineno-8-2 name=__codelineno-8-2 href=#__codelineno-8-2></a><span class=w> </span><span class=nx>it</span><span class=p>.</span><span class=nx>only</span><span class=p>(</span><span class=s2>"specific test case"</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-8-3><a id=__codelineno-8-3 name=__codelineno-8-3 href=#__codelineno-8-3></a><span class=w> </span><span class=c1>// Only this test will run</span>
|
|
</span><span id=__span-8-4><a id=__codelineno-8-4 name=__codelineno-8-4 href=#__codelineno-8-4></a><span class=w> </span><span class=p>});</span>
|
|
</span><span id=__span-8-5><a id=__codelineno-8-5 name=__codelineno-8-5 href=#__codelineno-8-5></a><span class=p>});</span>
|
|
</span></code></pre></div> <h2 id=writing-tests>Writing Tests</h2> <h3 id=test-file-naming>Test File Naming</h3> <ul> <li>Place test files in a <code>__tests__</code> directory adjacent to the code being tested.</li> <li>Name files with the pattern <code>*.test.ts</code>.</li> <li>Mirror the structure of the source code in your test organization.</li> </ul> <h3 id=example-test-structure>Example Test Structure</h3> <div class="language-typescript highlight"><pre><span></span><code><span id=__span-9-1><a id=__codelineno-9-1 name=__codelineno-9-1 href=#__codelineno-9-1></a><span class=nx>describe</span><span class=p>(</span><span class=s2>"Security Features"</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-9-2><a id=__codelineno-9-2 name=__codelineno-9-2 href=#__codelineno-9-2></a><span class=w> </span><span class=nx>it</span><span class=p>(</span><span class=s2>"should validate tokens correctly"</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-9-3><a id=__codelineno-9-3 name=__codelineno-9-3 href=#__codelineno-9-3></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>payload</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>userId</span><span class=o>:</span><span class=w> </span><span class=s2>"123"</span><span class=p>,</span><span class=w> </span><span class=nx>role</span><span class=o>:</span><span class=w> </span><span class=s2>"user"</span><span class=w> </span><span class=p>};</span>
|
|
</span><span id=__span-9-4><a id=__codelineno-9-4 name=__codelineno-9-4 href=#__codelineno-9-4></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>token</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=nx>jwt</span><span class=p>.</span><span class=nx>sign</span><span class=p>(</span><span class=nx>payload</span><span class=p>,</span><span class=w> </span><span class=nx>validSecret</span><span class=p>,</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=nx>expiresIn</span><span class=o>:</span><span class=w> </span><span class=s2>"1h"</span><span class=w> </span><span class=p>});</span>
|
|
</span><span id=__span-9-5><a id=__codelineno-9-5 name=__codelineno-9-5 href=#__codelineno-9-5></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>result</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=nx>TokenManager</span><span class=p>.</span><span class=nx>validateToken</span><span class=p>(</span><span class=nx>token</span><span class=p>,</span><span class=w> </span><span class=nx>testIp</span><span class=p>);</span>
|
|
</span><span id=__span-9-6><a id=__codelineno-9-6 name=__codelineno-9-6 href=#__codelineno-9-6></a><span class=w> </span><span class=nx>expect</span><span class=p>(</span><span class=nx>result</span><span class=p>.</span><span class=nx>valid</span><span class=p>).</span><span class=nx>toBe</span><span class=p>(</span><span class=kc>true</span><span class=p>);</span>
|
|
</span><span id=__span-9-7><a id=__codelineno-9-7 name=__codelineno-9-7 href=#__codelineno-9-7></a><span class=w> </span><span class=p>});</span>
|
|
</span><span id=__span-9-8><a id=__codelineno-9-8 name=__codelineno-9-8 href=#__codelineno-9-8></a><span class=p>});</span>
|
|
</span></code></pre></div> <h2 id=coverage>Coverage</h2> <p>The project maintains strict coverage: - Overall coverage: at least 80% - Critical paths: 90%+ - New features: ≥85% coverage</p> <p>Generate a coverage report with:</p> <div class="language-bash highlight"><pre><span></span><code><span id=__span-10-1><a id=__codelineno-10-1 name=__codelineno-10-1 href=#__codelineno-10-1></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--coverage
|
|
</span></code></pre></div> <h2 id=security-middleware-testing>Security Middleware Testing</h2> <h3 id=utility-function-testing>Utility Function Testing</h3> <p>The security middleware now uses a utility-first approach, which allows for more granular and comprehensive testing. Each security function is now independently testable, improving code reliability and maintainability.</p> <h4 id=key-utility-functions>Key Utility Functions</h4> <ol> <li><strong>Rate Limiting (<code>checkRateLimit</code>)</strong></li> <li>Tests multiple scenarios:<ul> <li>Requests under threshold</li> <li>Requests exceeding threshold</li> <li>Rate limit reset after window expiration</li> </ul> </li> </ol> <div class="language-typescript highlight"><pre><span></span><code><span id=__span-11-1><a id=__codelineno-11-1 name=__codelineno-11-1 href=#__codelineno-11-1></a><span class=c1>// Example test</span>
|
|
</span><span id=__span-11-2><a id=__codelineno-11-2 name=__codelineno-11-2 href=#__codelineno-11-2></a><span class=nx>it</span><span class=p>(</span><span class=s1>'should throw when requests exceed threshold'</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-11-3><a id=__codelineno-11-3 name=__codelineno-11-3 href=#__codelineno-11-3></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>ip</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=s1>'127.0.0.2'</span><span class=p>;</span>
|
|
</span><span id=__span-11-4><a id=__codelineno-11-4 name=__codelineno-11-4 href=#__codelineno-11-4></a><span class=w> </span><span class=k>for</span><span class=w> </span><span class=p>(</span><span class=kd>let</span><span class=w> </span><span class=nx>i</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=mf>0</span><span class=p>;</span><span class=w> </span><span class=nx>i</span><span class=w> </span><span class=o><</span><span class=w> </span><span class=mf>11</span><span class=p>;</span><span class=w> </span><span class=nx>i</span><span class=o>++</span><span class=p>)</span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-11-5><a id=__codelineno-11-5 name=__codelineno-11-5 href=#__codelineno-11-5></a><span class=w> </span><span class=k>if</span><span class=w> </span><span class=p>(</span><span class=nx>i</span><span class=w> </span><span class=o><</span><span class=w> </span><span class=mf>10</span><span class=p>)</span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-11-6><a id=__codelineno-11-6 name=__codelineno-11-6 href=#__codelineno-11-6></a><span class=w> </span><span class=nx>expect</span><span class=p>(()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=nx>checkRateLimit</span><span class=p>(</span><span class=nx>ip</span><span class=p>,</span><span class=w> </span><span class=mf>10</span><span class=p>)).</span><span class=nx>not</span><span class=p>.</span><span class=nx>toThrow</span><span class=p>();</span>
|
|
</span><span id=__span-11-7><a id=__codelineno-11-7 name=__codelineno-11-7 href=#__codelineno-11-7></a><span class=w> </span><span class=p>}</span><span class=w> </span><span class=k>else</span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-11-8><a id=__codelineno-11-8 name=__codelineno-11-8 href=#__codelineno-11-8></a><span class=w> </span><span class=nx>expect</span><span class=p>(()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=nx>checkRateLimit</span><span class=p>(</span><span class=nx>ip</span><span class=p>,</span><span class=w> </span><span class=mf>10</span><span class=p>)).</span><span class=nx>toThrow</span><span class=p>(</span><span class=s1>'Too many requests from this IP'</span><span class=p>);</span>
|
|
</span><span id=__span-11-9><a id=__codelineno-11-9 name=__codelineno-11-9 href=#__codelineno-11-9></a><span class=w> </span><span class=p>}</span>
|
|
</span><span id=__span-11-10><a id=__codelineno-11-10 name=__codelineno-11-10 href=#__codelineno-11-10></a><span class=w> </span><span class=p>}</span>
|
|
</span><span id=__span-11-11><a id=__codelineno-11-11 name=__codelineno-11-11 href=#__codelineno-11-11></a><span class=p>});</span>
|
|
</span></code></pre></div> <ol> <li><strong>Request Validation (<code>validateRequestHeaders</code>)</strong></li> <li>Tests content type validation</li> <li>Checks request size limits</li> <li>Validates authorization headers</li> </ol> <div class="language-typescript highlight"><pre><span></span><code><span id=__span-12-1><a id=__codelineno-12-1 name=__codelineno-12-1 href=#__codelineno-12-1></a><span class=nx>it</span><span class=p>(</span><span class=s1>'should reject invalid content type'</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-12-2><a id=__codelineno-12-2 name=__codelineno-12-2 href=#__codelineno-12-2></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>mockRequest</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=ow>new</span><span class=w> </span><span class=nx>Request</span><span class=p>(</span><span class=s1>'http://localhost'</span><span class=p>,</span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-12-3><a id=__codelineno-12-3 name=__codelineno-12-3 href=#__codelineno-12-3></a><span class=w> </span><span class=nx>method</span><span class=o>:</span><span class=w> </span><span class=s1>'POST'</span><span class=p>,</span>
|
|
</span><span id=__span-12-4><a id=__codelineno-12-4 name=__codelineno-12-4 href=#__codelineno-12-4></a><span class=w> </span><span class=nx>headers</span><span class=o>:</span><span class=w> </span><span class=p>{</span><span class=w> </span><span class=s1>'content-type'</span><span class=o>:</span><span class=w> </span><span class=s1>'text/plain'</span><span class=w> </span><span class=p>}</span>
|
|
</span><span id=__span-12-5><a id=__codelineno-12-5 name=__codelineno-12-5 href=#__codelineno-12-5></a><span class=w> </span><span class=p>});</span>
|
|
</span><span id=__span-12-6><a id=__codelineno-12-6 name=__codelineno-12-6 href=#__codelineno-12-6></a><span class=w> </span><span class=nx>expect</span><span class=p>(()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=nx>validateRequestHeaders</span><span class=p>(</span><span class=nx>mockRequest</span><span class=p>)).</span><span class=nx>toThrow</span><span class=p>(</span><span class=s1>'Content-Type must be application/json'</span><span class=p>);</span>
|
|
</span><span id=__span-12-7><a id=__codelineno-12-7 name=__codelineno-12-7 href=#__codelineno-12-7></a><span class=p>});</span>
|
|
</span></code></pre></div> <ol> <li><strong>Input Sanitization (<code>sanitizeValue</code>)</strong></li> <li>Sanitizes HTML tags</li> <li>Handles nested objects</li> <li>Preserves non-string values</li> </ol> <div class="language-typescript highlight"><pre><span></span><code><span id=__span-13-1><a id=__codelineno-13-1 name=__codelineno-13-1 href=#__codelineno-13-1></a><span class=nx>it</span><span class=p>(</span><span class=s1>'should sanitize HTML tags'</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-13-2><a id=__codelineno-13-2 name=__codelineno-13-2 href=#__codelineno-13-2></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>input</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=s1>'<script>alert("xss")</script>Hello'</span><span class=p>;</span>
|
|
</span><span id=__span-13-3><a id=__codelineno-13-3 name=__codelineno-13-3 href=#__codelineno-13-3></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>sanitized</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=nx>sanitizeValue</span><span class=p>(</span><span class=nx>input</span><span class=p>);</span>
|
|
</span><span id=__span-13-4><a id=__codelineno-13-4 name=__codelineno-13-4 href=#__codelineno-13-4></a><span class=w> </span><span class=nx>expect</span><span class=p>(</span><span class=nx>sanitized</span><span class=p>).</span><span class=nx>toBe</span><span class=p>(</span><span class=s1>'&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;Hello'</span><span class=p>);</span>
|
|
</span><span id=__span-13-5><a id=__codelineno-13-5 name=__codelineno-13-5 href=#__codelineno-13-5></a><span class=p>});</span>
|
|
</span></code></pre></div> <ol> <li><strong>Security Headers (<code>applySecurityHeaders</code>)</strong></li> <li>Verifies correct security header application</li> <li>Checks CSP, frame options, and other security headers</li> </ol> <div class="language-typescript highlight"><pre><span></span><code><span id=__span-14-1><a id=__codelineno-14-1 name=__codelineno-14-1 href=#__codelineno-14-1></a><span class=nx>it</span><span class=p>(</span><span class=s1>'should apply security headers'</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-14-2><a id=__codelineno-14-2 name=__codelineno-14-2 href=#__codelineno-14-2></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>mockRequest</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=ow>new</span><span class=w> </span><span class=nx>Request</span><span class=p>(</span><span class=s1>'http://localhost'</span><span class=p>);</span>
|
|
</span><span id=__span-14-3><a id=__codelineno-14-3 name=__codelineno-14-3 href=#__codelineno-14-3></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>headers</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=nx>applySecurityHeaders</span><span class=p>(</span><span class=nx>mockRequest</span><span class=p>);</span>
|
|
</span><span id=__span-14-4><a id=__codelineno-14-4 name=__codelineno-14-4 href=#__codelineno-14-4></a><span class=w> </span><span class=nx>expect</span><span class=p>(</span><span class=nx>headers</span><span class=p>[</span><span class=s1>'content-security-policy'</span><span class=p>]).</span><span class=nx>toBeDefined</span><span class=p>();</span>
|
|
</span><span id=__span-14-5><a id=__codelineno-14-5 name=__codelineno-14-5 href=#__codelineno-14-5></a><span class=w> </span><span class=nx>expect</span><span class=p>(</span><span class=nx>headers</span><span class=p>[</span><span class=s1>'x-frame-options'</span><span class=p>]).</span><span class=nx>toBeDefined</span><span class=p>();</span>
|
|
</span><span id=__span-14-6><a id=__codelineno-14-6 name=__codelineno-14-6 href=#__codelineno-14-6></a><span class=p>});</span>
|
|
</span></code></pre></div> <ol> <li><strong>Error Handling (<code>handleError</code>)</strong></li> <li>Tests error responses in production and development modes</li> <li>Verifies error message and stack trace inclusion</li> </ol> <div class="language-typescript highlight"><pre><span></span><code><span id=__span-15-1><a id=__codelineno-15-1 name=__codelineno-15-1 href=#__codelineno-15-1></a><span class=nx>it</span><span class=p>(</span><span class=s1>'should include error details in development mode'</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-15-2><a id=__codelineno-15-2 name=__codelineno-15-2 href=#__codelineno-15-2></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>error</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=ow>new</span><span class=w> </span><span class=ne>Error</span><span class=p>(</span><span class=s1>'Test error'</span><span class=p>);</span>
|
|
</span><span id=__span-15-3><a id=__codelineno-15-3 name=__codelineno-15-3 href=#__codelineno-15-3></a><span class=w> </span><span class=kd>const</span><span class=w> </span><span class=nx>result</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=nx>handleError</span><span class=p>(</span><span class=nx>error</span><span class=p>,</span><span class=w> </span><span class=s1>'development'</span><span class=p>);</span>
|
|
</span><span id=__span-15-4><a id=__codelineno-15-4 name=__codelineno-15-4 href=#__codelineno-15-4></a><span class=w> </span><span class=nx>expect</span><span class=p>(</span><span class=nx>result</span><span class=p>).</span><span class=nx>toEqual</span><span class=p>({</span>
|
|
</span><span id=__span-15-5><a id=__codelineno-15-5 name=__codelineno-15-5 href=#__codelineno-15-5></a><span class=w> </span><span class=nx>error</span><span class=o>:</span><span class=w> </span><span class=kt>true</span><span class=p>,</span>
|
|
</span><span id=__span-15-6><a id=__codelineno-15-6 name=__codelineno-15-6 href=#__codelineno-15-6></a><span class=w> </span><span class=nx>message</span><span class=o>:</span><span class=w> </span><span class=s1>'Internal server error'</span><span class=p>,</span>
|
|
</span><span id=__span-15-7><a id=__codelineno-15-7 name=__codelineno-15-7 href=#__codelineno-15-7></a><span class=w> </span><span class=nx>error</span><span class=o>:</span><span class=w> </span><span class=s1>'Test error'</span><span class=p>,</span>
|
|
</span><span id=__span-15-8><a id=__codelineno-15-8 name=__codelineno-15-8 href=#__codelineno-15-8></a><span class=w> </span><span class=nx>stack</span><span class=o>:</span><span class=w> </span><span class=kt>expect.any</span><span class=p>(</span><span class=nb>String</span><span class=p>)</span>
|
|
</span><span id=__span-15-9><a id=__codelineno-15-9 name=__codelineno-15-9 href=#__codelineno-15-9></a><span class=w> </span><span class=p>});</span>
|
|
</span><span id=__span-15-10><a id=__codelineno-15-10 name=__codelineno-15-10 href=#__codelineno-15-10></a><span class=p>});</span>
|
|
</span></code></pre></div> <h3 id=testing-philosophy>Testing Philosophy</h3> <ul> <li><strong>Isolation</strong>: Each utility function is tested independently</li> <li><strong>Comprehensive Coverage</strong>: Multiple scenarios for each function</li> <li><strong>Predictable Behavior</strong>: Clear expectations for input and output</li> <li><strong>Error Handling</strong>: Robust testing of error conditions</li> </ul> <h3 id=best-practices>Best Practices</h3> <ol> <li>Use minimal, focused test cases</li> <li>Test both successful and failure scenarios</li> <li>Verify input sanitization and security measures</li> <li>Mock external dependencies when necessary</li> </ol> <h3 id=running-security-tests>Running Security Tests</h3> <div class="language-bash highlight"><pre><span></span><code><span id=__span-16-1><a id=__codelineno-16-1 name=__codelineno-16-1 href=#__codelineno-16-1></a><span class=c1># Run all tests</span>
|
|
</span><span id=__span-16-2><a id=__codelineno-16-2 name=__codelineno-16-2 href=#__codelineno-16-2></a>bun<span class=w> </span><span class=nb>test</span>
|
|
</span><span id=__span-16-3><a id=__codelineno-16-3 name=__codelineno-16-3 href=#__codelineno-16-3></a>
|
|
</span><span id=__span-16-4><a id=__codelineno-16-4 name=__codelineno-16-4 href=#__codelineno-16-4></a><span class=c1># Run specific security tests</span>
|
|
</span><span id=__span-16-5><a id=__codelineno-16-5 name=__codelineno-16-5 href=#__codelineno-16-5></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>__tests__/security/
|
|
</span></code></pre></div> <h3 id=continuous-improvement>Continuous Improvement</h3> <ul> <li>Regularly update test cases</li> <li>Add new test scenarios as security requirements evolve</li> <li>Perform periodic security audits</li> </ul> <h2 id=best-practices_1>Best Practices</h2> <ol> <li><strong>Isolation</strong>: Each test should be independent and not rely on the state of other tests.</li> <li><strong>Mocking</strong>: Use the provided mock utilities for external dependencies.</li> <li><strong>Cleanup</strong>: Clean up any resources or state modifications in <code>afterEach</code> or <code>afterAll</code> hooks.</li> <li><strong>Descriptive Names</strong>: Use clear, descriptive test names that explain the expected behavior.</li> <li><strong>Assertions</strong>: Make specific, meaningful assertions rather than general ones.</li> <li><strong>Setup</strong>: Use <code>beforeEach</code> for common test setup to avoid repetition.</li> <li><strong>Error Cases</strong>: Test both success and error cases for complete coverage.</li> </ol> <h2 id=coverage_1>Coverage</h2> <p>The project aims for high test coverage, particularly focusing on: - Security-critical code paths - API endpoints - Data validation - Error handling - Event broadcasting</p> <p>Run coverage reports using: <div class="language-bash highlight"><pre><span></span><code><span id=__span-17-1><a id=__codelineno-17-1 name=__codelineno-17-1 href=#__codelineno-17-1></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--coverage
|
|
</span></code></pre></div></p> <h2 id=debugging-tests>Debugging Tests</h2> <p>To debug tests: 1. Set <code>DEBUG=true</code> to enable console output during tests 2. Use the <code>--watch</code> flag for development 3. Add <code>console.log()</code> statements (they're only shown when DEBUG is true) 4. Use the test utilities' debugging helpers</p> <h3 id=advanced-debugging_1>Advanced Debugging</h3> <ol> <li> <p><strong>Using Node Inspector</strong>: <div class="language-bash highlight"><pre><span></span><code><span id=__span-18-1><a id=__codelineno-18-1 name=__codelineno-18-1 href=#__codelineno-18-1></a><span class=c1># Start tests with inspector</span>
|
|
</span><span id=__span-18-2><a id=__codelineno-18-2 name=__codelineno-18-2 href=#__codelineno-18-2></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--inspect
|
|
</span><span id=__span-18-3><a id=__codelineno-18-3 name=__codelineno-18-3 href=#__codelineno-18-3></a>
|
|
</span><span id=__span-18-4><a id=__codelineno-18-4 name=__codelineno-18-4 href=#__codelineno-18-4></a><span class=c1># Start tests with inspector and break on first line</span>
|
|
</span><span id=__span-18-5><a id=__codelineno-18-5 name=__codelineno-18-5 href=#__codelineno-18-5></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--inspect-brk
|
|
</span></code></pre></div></p> </li> <li> <p><strong>Using VS Code</strong>: <div class="language-text highlight"><pre><span></span><code><span id=__span-19-1><a id=__codelineno-19-1 name=__codelineno-19-1 href=#__codelineno-19-1></a>// .vscode/launch.json
|
|
</span><span id=__span-19-2><a id=__codelineno-19-2 name=__codelineno-19-2 href=#__codelineno-19-2></a>{
|
|
</span><span id=__span-19-3><a id=__codelineno-19-3 name=__codelineno-19-3 href=#__codelineno-19-3></a> "version": "0.2.0",
|
|
</span><span id=__span-19-4><a id=__codelineno-19-4 name=__codelineno-19-4 href=#__codelineno-19-4></a> "configurations": [
|
|
</span><span id=__span-19-5><a id=__codelineno-19-5 name=__codelineno-19-5 href=#__codelineno-19-5></a> {
|
|
</span><span id=__span-19-6><a id=__codelineno-19-6 name=__codelineno-19-6 href=#__codelineno-19-6></a> "type": "bun",
|
|
</span><span id=__span-19-7><a id=__codelineno-19-7 name=__codelineno-19-7 href=#__codelineno-19-7></a> "request": "launch",
|
|
</span><span id=__span-19-8><a id=__codelineno-19-8 name=__codelineno-19-8 href=#__codelineno-19-8></a> "name": "Debug Tests",
|
|
</span><span id=__span-19-9><a id=__codelineno-19-9 name=__codelineno-19-9 href=#__codelineno-19-9></a> "program": "${workspaceFolder}/node_modules/bun/bin/bun",
|
|
</span><span id=__span-19-10><a id=__codelineno-19-10 name=__codelineno-19-10 href=#__codelineno-19-10></a> "args": ["test", "${file}"],
|
|
</span><span id=__span-19-11><a id=__codelineno-19-11 name=__codelineno-19-11 href=#__codelineno-19-11></a> "cwd": "${workspaceFolder}",
|
|
</span><span id=__span-19-12><a id=__codelineno-19-12 name=__codelineno-19-12 href=#__codelineno-19-12></a> "env": { "DEBUG": "true" }
|
|
</span><span id=__span-19-13><a id=__codelineno-19-13 name=__codelineno-19-13 href=#__codelineno-19-13></a> }
|
|
</span><span id=__span-19-14><a id=__codelineno-19-14 name=__codelineno-19-14 href=#__codelineno-19-14></a> ]
|
|
</span><span id=__span-19-15><a id=__codelineno-19-15 name=__codelineno-19-15 href=#__codelineno-19-15></a>}
|
|
</span></code></pre></div></p> </li> <li> <p><strong>Test Isolation</strong>: To run a single test in isolation: <div class="language-typescript highlight"><pre><span></span><code><span id=__span-20-1><a id=__codelineno-20-1 name=__codelineno-20-1 href=#__codelineno-20-1></a><span class=nx>describe</span><span class=p>.</span><span class=nx>only</span><span class=p>(</span><span class=s2>"specific test suite"</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-20-2><a id=__codelineno-20-2 name=__codelineno-20-2 href=#__codelineno-20-2></a><span class=w> </span><span class=nx>it</span><span class=p>.</span><span class=nx>only</span><span class=p>(</span><span class=s2>"specific test case"</span><span class=p>,</span><span class=w> </span><span class=p>()</span><span class=w> </span><span class=p>=></span><span class=w> </span><span class=p>{</span>
|
|
</span><span id=__span-20-3><a id=__codelineno-20-3 name=__codelineno-20-3 href=#__codelineno-20-3></a><span class=w> </span><span class=c1>// Only this test will run</span>
|
|
</span><span id=__span-20-4><a id=__codelineno-20-4 name=__codelineno-20-4 href=#__codelineno-20-4></a><span class=w> </span><span class=p>});</span>
|
|
</span><span id=__span-20-5><a id=__codelineno-20-5 name=__codelineno-20-5 href=#__codelineno-20-5></a><span class=p>});</span>
|
|
</span></code></pre></div></p> </li> </ol> <h2 id=contributing>Contributing</h2> <p>When contributing new code: 1. Add tests for new features 2. Ensure existing tests pass 3. Maintain or improve coverage 4. Follow the existing test patterns and naming conventions 5. Document any new test utilities or patterns </p> <h2 id=coverage-requirements>Coverage Requirements</h2> <p>The project maintains strict coverage requirements:</p> <ul> <li>Minimum overall coverage: 80%</li> <li>Critical paths (security, API, data validation): 90%</li> <li>New features must include tests with >= 85% coverage</li> </ul> <p>Coverage reports are generated in multiple formats: - Console summary - HTML report (./coverage/index.html) - LCOV report (./coverage/lcov.info)</p> <p>To view detailed coverage: <div class="language-bash highlight"><pre><span></span><code><span id=__span-21-1><a id=__codelineno-21-1 name=__codelineno-21-1 href=#__codelineno-21-1></a><span class=c1># Generate and open coverage report</span>
|
|
</span><span id=__span-21-2><a id=__codelineno-21-2 name=__codelineno-21-2 href=#__codelineno-21-2></a>bun<span class=w> </span><span class=nb>test</span><span class=w> </span>--coverage<span class=w> </span><span class=o>&&</span><span class=w> </span>open<span class=w> </span>coverage/index.html
|
|
</span></code></pre></div></p> <aside class=md-source-file> <span class=md-source-file__fact> <span class=md-icon title="Last update"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1zM12.5 7v5.2l4 2.4-1 1L11 13V7zM11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">February 4, 2025</span> </span> <span class=md-source-file__fact> <span class=md-icon title=Created> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M14.47 15.08 11 13V7h1.5v5.25l3.08 1.83c-.41.28-.79.62-1.11 1m-1.39 4.84c-.36.05-.71.08-1.08.08-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8c0 .37-.03.72-.08 1.08.69.1 1.33.32 1.92.64.1-.56.16-1.13.16-1.72 0-5.5-4.5-10-10-10S2 6.5 2 12s4.47 10 10 10c.59 0 1.16-.06 1.72-.16-.32-.59-.54-1.23-.64-1.92M18 15v3h-3v2h3v3h2v-3h3v-2h-3v-3z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">February 4, 2025</span> </span> </aside> <form class=md-feedback name=feedback hidden> <fieldset> <legend class=md-feedback__title> Was this page helpful? </legend> <div class=md-feedback__inner> <div class=md-feedback__list> <button class="md-feedback__icon md-icon" type=submit title="This page was helpful" data-md-value=1> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M20 12a8 8 0 0 0-8-8 8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8m2 0a10 10 0 0 1-10 10A10 10 0 0 1 2 12 10 10 0 0 1 12 2a10 10 0 0 1 10 10M10 9.5c0 .8-.7 1.5-1.5 1.5S7 10.3 7 9.5 7.7 8 8.5 8s1.5.7 1.5 1.5m7 0c0 .8-.7 1.5-1.5 1.5S14 10.3 14 9.5 14.7 8 15.5 8s1.5.7 1.5 1.5m-5 7.73c-1.75 0-3.29-.73-4.19-1.81L9.23 14c.45.72 1.52 1.23 2.77 1.23s2.32-.51 2.77-1.23l1.42 1.42c-.9 1.08-2.44 1.81-4.19 1.81"/></svg> </button> <button class="md-feedback__icon md-icon" type=submit title="This page could be improved" data-md-value=0> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M20 12a8 8 0 0 0-8-8 8 8 0 0 0-8 8 8 8 0 0 0 8 8 8 8 0 0 0 8-8m2 0a10 10 0 0 1-10 10A10 10 0 0 1 2 12 10 10 0 0 1 12 2a10 10 0 0 1 10 10m-6.5-4c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5-1.5-.7-1.5-1.5.7-1.5 1.5-1.5M10 9.5c0 .8-.7 1.5-1.5 1.5S7 10.3 7 9.5 7.7 8 8.5 8s1.5.7 1.5 1.5m2 4.5c1.75 0 3.29.72 4.19 1.81l-1.42 1.42C14.32 16.5 13.25 16 12 16s-2.32.5-2.77 1.23l-1.42-1.42C8.71 14.72 10.25 14 12 14"/></svg> </button> </div> <div class=md-feedback__note> <div data-md-value=1 hidden> Thanks for your feedback! </div> <div data-md-value=0 hidden> Thanks for your feedback! Please consider creating an issue to help us improve. </div> </div> </div> </fieldset> </form> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> </main> <footer class=md-footer> <nav class="md-footer__inner md-grid" aria-label=Footer> <a href=contributing.html class="md-footer__link md-footer__link--prev" aria-label="Previous: Contributing"> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg> </div> <div class=md-footer__title> <span class=md-footer__direction> Previous </span> <div class=md-ellipsis> Contributing </div> </div> </a> <a href=development/best-practices.html class="md-footer__link md-footer__link--next" aria-label="Next: Best Practices"> <div class=md-footer__title> <span class=md-footer__direction> Next </span> <div class=md-ellipsis> Best Practices </div> </div> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg> </div> </a> </nav> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> <div class=md-copyright__highlight> Copyright © 2025 jango-blockchained </div> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener> Material for MkDocs </a> </div> <div class=md-social> <a href=https://github.com/jango-blockchained/homeassistant-mcp target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 496 512"><!-- Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> <a href=https://hub.docker.com/r/jangoblockchained/homeassistant-mcp target=_blank rel=noopener title=hub.docker.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 640 512"><!-- Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M349.9 236.3h-66.1v-59.4h66.1zm0-204.3h-66.1v60.7h66.1zm78.2 144.8H362v59.4h66.1zm-156.3-72.1h-66.1v60.1h66.1zm78.1 0h-66.1v60.1h66.1zm276.8 100c-14.4-9.7-47.6-13.2-73.1-8.4-3.3-24-16.7-44.9-41.1-63.7l-14-9.3-9.3 14c-18.4 27.8-23.4 73.6-3.7 103.8-8.7 4.7-25.8 11.1-48.4 10.7H2.4c-8.7 50.8 5.8 116.8 44 162.1 37.1 43.9 92.7 66.2 165.4 66.2 157.4 0 273.9-72.5 328.4-204.2 21.4.4 67.6.1 91.3-45.2 1.5-2.5 6.6-13.2 8.5-17.1zm-511.1-27.9h-66v59.4h66.1v-59.4zm78.1 0h-66.1v59.4h66.1zm78.1 0h-66.1v59.4h66.1zm-78.1-72.1h-66.1v60.1h66.1z"/></svg> </a> </div> </div> </div> </footer> </div> <div class=md-dialog data-md-component=dialog> <div class="md-dialog__inner md-typeset"></div> </div> <div class=md-consent data-md-component=consent id=__consent hidden> <div class=md-consent__overlay></div> <aside class=md-consent__inner> <form class="md-consent__form md-grid md-typeset" name=consent> <h4>Cookie consent</h4> <p>We use cookies to recognize your repeated visits and preferences, as well as to measure the effectiveness of our documentation and whether users find what they're searching for. With your consent, you're helping us to make our documentation better.</p> <input class=md-toggle type=checkbox id=__settings> <div class=md-consent__settings> <ul class=task-list> <li class=task-list-item> <label class=task-list-control> <input type=checkbox name=analytics checked> <span class=task-list-indicator></span> Google Analytics </label> </li> <li class=task-list-item> <label class=task-list-control> <input type=checkbox name=github checked> <span class=task-list-indicator></span> GitHub </label> </li> </ul> </div> <div class=md-consent__controls> <button class="md-button md-button--primary">Accept</button> <button type=reset class="md-button md-button--primary">Reject</button> <label class=md-button for=__settings>Manage settings</label> </div> </form> </aside> </div> <script>var consent=__md_get("__consent");if(consent)for(var input of document.forms.consent.elements)input.name&&(input.checked=consent[input.name]||!1);else"file:"!==location.protocol&&setTimeout((function(){document.querySelector("[data-md-component=consent]").hidden=!1}),250);var form=document.forms.consent;for(var action of["submit","reset"])form.addEventListener(action,(function(e){if(e.preventDefault(),"reset"===e.type)for(var n of document.forms.consent.elements)n.name&&(n.checked=!1);__md_set("__consent",Object.fromEntries(Array.from(new FormData(form).keys()).map((function(e){return[e,!0]})))),location.hash="",location.reload()}))</script> <script id=__config type=application/json>{"base": ".", "features": ["navigation.tabs", "navigation.tabs.sticky", "navigation.indexes", "navigation.sections", "navigation.expand", "navigation.path", "navigation.footer", "navigation.prune", "navigation.tracking", "navigation.instant", "header.autohide", "toc.integrate", "toc.follow", "announce.dismiss", "search.suggest", "search.highlight", "search.share", "content.code.annotate", "content.code.copy", "content.code.select", "content.tabs.link", "content.tooltips"], "search": "assets/javascripts/workers/search.f8cc74c7.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": {"default": "latest", "provider": "mike"}}</script> <script src=assets/javascripts/bundle.f13b1293.min.js></script> <script src=javascripts/mathjax.js></script> <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script> <script src=https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js></script> <script src=javascripts/extra.js></script> </body> </html> |