# Probar servicios (WKC)

Los servicios de componentes well known son servidores construidos con el [well known component](https://well-known-components.github.io/documentation/) arquitectura, donde cada parte del servicio está modularizada y encapsulada en un componente. Estos componentes, junto con la capacidad de ser fácilmente intercambiables, tienen la capacidad de ser fáciles de probar unitariamente, ya que todas sus dependencias se inyectan cuando se crean.

Las pruebas de estos servicios DEBERÍAN realizarse usando dos tipos diferentes de prueba, **unitaria** y **integración**. Los componentes que contienen lógica DEBEN probarse mediante pruebas unitarias y los componentes que tienen principalmente interacciones con servicios externos DEBERÍAN probarse mediante pruebas de integración. Es decir, un componente adaptador de base de datos que no contiene lógica y contiene principalmente consultas a la base de datos DEBE ser probado de forma integrada.

## Pruebas unitarias de componentes well known component

Todos los componentes DEBEN ser probados unitariamente y DEBEN escribirse siguiendo la sección definida aquí sobre cómo escribir pruebas.

Para mostrar cómo se prueban estos componentes, aquí hay un componente simple con solo un método y dos dependencias, **settings** y **friends**:

```tsx
// Componente de voz

export function createVoiceComponent(dependencies: Pick<AppComponents, 'settings' | 'friends'>) {
	const { settings, friends } = dependencies

  async function canCallEachOther(caller: string, callee: string): Promise<boolean> {
	  const [callerSettings, calleeSetting] = Promise.all([settings.getPrivacySetting(caller), settings.getPrivacySetting(callee)])
	  const areFriends = await friends.areUsersFriends(caller, callee)
		return areFriends || (callerSettings !== Settings.ONLY_FRIENDS && calleeSettings !== Settings.ONLY_FRIENDS)
  }
  
  return {
	  createVoiceChat
  }
}
```

Para probar este componente, se deben construir mocks apropiados, es decir, mocks que tengan los tipos correctos para interactuar con el componente que se va a probar y que sean flexibles para permitirnos crear todo tipo de pruebas. Estos mocks también deben ser inmutables entre pruebas, preservando la idea de que cada prueba se ejecuta en su propio contexto, aislada de las demás.

Estos mocks DEBERÍAN crearse usando funciones creadoras de mocks de componentes y DEBERÍAN nombrarse como create**ComponentName**MockedComponent. Tomando como ejemplo el componente creado arriba, aquí hay un ejemplo de cómo DEBERÍAN verse:

```tsx
// Colocado en el archivo /test/mocks/settings.ts
export function createSettingsMockedComponent(overrides?: Partial<jest.Mocked<ISettingsComponent>>): jest.Mocked<ISettingsComponent> {
  return {
    getPrivacySettings: overrides?.getPrivacySettings ?? jest.fn()
  }
}

// Colocado en el archivo test/mocks/friends.ts
export function createFriendsMockedComponent(overrides?: Partial<jest.Mocked<IFriendsComponent>>): jest.Mocked<IFriendsComponent> {
	return {
	  // Tenemos la flexibilidad de definir el comportamiento en cualquiera de los métodos exportados por el componente.
	  areUsersFriends: overrides?.areUsersFriends ?? jest.fn(),
	  // Los métodos que no se usan pueden omitirse en el parámetro overrides, convirtiéndolos en un simple mock de función.
	  hasBlockedUser: overrides?.hasBlockedUser ?? jest.fn()
	}
}
```

Al usar estos mocks colocados cuidadosamente dentro de un `beforeEach`, podemos estar seguros de que los mocks están definidos correctamente y aislados entre ejecuciones. Aquí hay un ejemplo de cómo se pueden usar estos mocks:

```tsx
// Define las funciones mockeadas en el contexto para que podamos cambiar su mock.
let getPrivacySettingsMock: jest.MockedFn<ISettingsComponent['getPrivacySettings']>
let areUsersFriendsMock: jest.MockedFn<IFriendsComponent['areUsersFriends']>
let voice: IVoiceComponent

beforeEach({
  getPrivacySettingsMock = jest.fn()
  areUsersFriendsMock = jest.fn()
  // Construye el mock
  const settings = createSettingsMockedComponent({ getPrivacySettings: getPrivacySettingsMock })
  // Inicializa el mock del componente con solo los métodos que se van a usar en las pruebas
  const friends = createFriendsMockedComponent({ areUsersFriends: areUsersFriendsMock })
  // Crea el componente a probar usando los mocks
  voice = createVoiceComponent({ settings, friends })
})
```

Esta forma de definir los mocks no solo nos permite aislar correctamente los contextos de las pruebas entre ejecuciones, sino que también establece las bases para una inicialización adecuada del contexto, ya que al tener las funciones mockeadas ya definidas, es bastante fácil construir cada contexto:

```tsx
// Definido después del bloque de código anterior.

describe('when checking if two users can call each other', () => {
	const calleeAddress = '0xd7D746d39D142b6bE752efd7626cE28F245a25D1'
	const callerAddress = '0x2e8b4De1230f827082202aa53d489A26163aace0'

  describe('and the callee only accepts calls from friends', () => {
    beforeEach(() => {
      // Define el mock para el contexto, haciendo que el callee 
      getPrivacySettingsMock.mockImplementation((address: string) => {
	      switch(address) {
	        case caleeAddress:
		        return Settings.ONLY_FRIENDS
		      case callerAddress:
			      return Settings.ALL
			    default:
				    throw new Error("Wrong mock")
	      }
      })
    })
    
    describe('and the calle and the caller are friends', () => {
	    beforeEach(() => {
		    areUsersFriendsMock.mockResolvedValueOnce(true)
	    })
	    
	    it('should resolve to true', () => {
		    return expect(voice.canCallEachOther(callerAddress, calleeAddress)).resolves.toBe(true)
	    })
    })
    
    describe('and the callee and the caller are not friends', () =>{
		  beforeEach(() => {
		    areUsersFriendsMock.mockResolvedValueOnce(false)
	    })
	    
	    it('should resolve to false', () => {
		    return expect(voice.canCallEachOther(callerAddress, calleeAddress)).resolves.toBe(false)
	    })
    })
  })
  
  //... otros contextos
})
```

## Pruebas de integración

Todos los endpoints, llamadas WS RPC, jobs o tareas DEBEN probarse integralmente y DEBEN escribirse siguiendo la sección definida aquí sobre cómo escribir pruebas. Las pruebas de integración deben colocarse bajo el `test/integration` directorio y deben nombrarse de acuerdo con el punto de entrada que queramos probar.

Las pruebas de integración deben limitarse a probar o bien condiciones específicas de integración que no pueden probarse con pruebas unitarias (consultas SQL, operaciones Redis, etc.) y la integración entre nuestros componentes lógicos y el protocolo (HTTP, WS).

Para mostrar un ejemplo de cómo deben realizarse las pruebas de integración, probaremos una solicitud HTTP simple que recupera los amigos de un usuario.

```tsx
export async function getFriendHandler(
  context: Pick<
    HandlerContextWithPath<'logs' | 'communities', '/v1/users/:user/friends'>,
    'url' | 'components' | 'params' | 'verification'
  >
): Promise<HTTPResponse<AggregatedCommunityWithMemberData>> {
  const {
    components: { friends },
    params: { id },
    verification
  } = context

  try {
    const userAddress = verification!.auth.toLowerCase()

    return {
      status: 200,
      body: {
        data: await friends.getFriends(userAddress)
      }
    }
  } catch (error) {
  
	  if (error instanceof InvalidFriendshipRequest) {
			throw InvalidRequest(isErrorWithMessage(error) ? error.message : 'Uknown error')
	  }

    return {
      status: 500,
      body: {
        message
      }
    }
  }
}
```

Y un componente friends que tenga lo siguiente:

```tsx
async function getFriends(addres: string) {
	friendsDb.getFriends()
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/testing-standards/testing-services-wkc.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
