Crowdsale Error when calling buyTokens() or just simply invoking fallback function

Hi community,
I am working on distributing my token via a crowdsale, using Allownace Crowdsale for better control of our token supply. I am using an ERC20 standard contract from the latest from Open Zeppelin and a crowdsale contract from v 2.5.0 since that is the latest that was supported. My contracts compile with no issues, and I am able to run a unit test that confirms, my approve() function works. I am able to approve tokens for the crowdsale contract to use. However, when I send Eth to the crowdsale contract or when I call buyTokens() directly, I am getting the following Error:


Form the Unit testing...

From my React App I am getting the following Error:

Uncaught (in promise) Error: invalid address (argument="address", value=undefined, code=INVALID_ARGUMENT, version=address/5.5.0) (argument="spender", value=undefined, code=INVALID_ARGUMENT, version=abi/5.0.7)

If any of you guys have seen this before and can help guide me to a viable solution would be greatly appreciated.

:1234: Code to reproduce

Here is my Unit testing script:

const RedBasicPresubs = artifacts.require('RedBasicPresubs');
const Red = artifacts.require('RED');

const chai = require('./setupchai.js');
const BN = web3.utils.BN;
const expect = chai.expect;

require('dotenv').config({ path: '../.env' });

contract('RedBasicPresubs Test', function (accounts) {
	const [deployerAccount, recipient, anotherAccount] = accounts;
	//console.log(recipient);

	it('Crowdsale Should have allowance of 1 billion RED tokens', async () => {
		let instance = await RedBasicPresubs.deployed();
		let redInstance = await Red.deployed();
		let balanceDep = await redInstance.balanceOf(deployerAccount);

		await redInstance.approve(instance.address, 1000000000);
		let balance = await instance.remainingTokens();
		let allowance = await redInstance.allowance(deployerAccount, instance.address);
		console.log(allowance.toString());
		expect(allowance).to.be.a.bignumber.equal(new BN(balance));
		return expect(balanceDep).to.be.a.bignumber.equal(new BN(100000000000));
	});

	it('should be possible to buy one token by simply sending ether to the smart contract', async () => {
		let instance = await RedBasicPresubs.deployed();
		let redInstance = await Red.deployed();
		//let allowance = await redInstance.allowance(instance.address);
		let balanceDep = await redInstance.balanceOf(deployerAccount);
		let recipienteRedBal = await redInstance.balanceOf(recipient);
		let purchaseAmount = await web3.utils.toWei('1000', 'wei');
		let balanceBefore = await instance.remainingTokens();
		console.log(balanceBefore.toString());

		//await redInstance.approve(instance.address, 100);
		let balanceAfter = await balanceBefore.add(new BN(1000000000));

		await expect(instance.sendTransaction({ from: recipient, purchaseAmount })).to.be.fulfilled;
		//await expect(instance.buyTokens({ from: recipient, value: purchaseAmount })).to.be.fulfilled;

		await expect(balanceAfter).to.be.a.bignumber.equal(new BN(balanceBefore + 1000000000));
		await expect(recipienteRedBal).to.be.a.bignumber.equal(new BN(purchaseAmount));
		return expect(instance.weiRaised()).to.eventually.be.a.bignumber.equal(new BN(1000));
	});

});

Here is the code for allowance Crowdsale Contract from Open Zeppelin:

pragma solidity ^0.5.0;

import "../Crowdsale.sol";
import "../../token/ERC20/IERC20.sol";
import "../../token/ERC20/SafeERC20.sol";
import "../../math/SafeMath.sol";
import "../../math/Math.sol";

/**
 * @title AllowanceCrowdsale
 * @dev Extension of Crowdsale where tokens are held by a wallet, which approves an allowance to the crowdsale.
 */
contract AllowanceCrowdsale is Crowdsale {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    address private _tokenWallet;

    /**
     * @dev Constructor, takes token wallet address.
     * @param tokenWallet Address holding the tokens, which has approved allowance to the crowdsale.
     */
    constructor (address tokenWallet) public {
        require(tokenWallet != address(0), "AllowanceCrowdsale: token wallet is the zero address");
        _tokenWallet = tokenWallet;
    }

    /**
     * @return the address of the wallet that will hold the tokens.
     */
    function tokenWallet() public view returns (address) {
        return _tokenWallet;
    }

    /**
     * @dev Checks the amount of tokens left in the allowance.
     * @return Amount of tokens left in the allowance
     */
    function remainingTokens() public view returns (uint256) {
        return Math.min(token().balanceOf(_tokenWallet), token().allowance(_tokenWallet, address(this)));
    }

    /**
     * @dev Overrides parent behavior by transferring tokens from wallet.
     * @param beneficiary Token purchaser
     * @param tokenAmount Amount of tokens purchased
     */
    function _deliverTokens(address beneficiary, uint256 tokenAmount) internal {
        token().safeTransferFrom(_tokenWallet, beneficiary, tokenAmount); 
    }
}

And here is my crowdsale contract:

pragma solidity ^0.5.0;

//import "@openzeppelin/contracts/crowdsale/Crowdsale.sol";
import "@openzeppelin/contracts/crowdsale/emission/AllowanceCrowdsale.sol";


  contract RedBasicPresubs is AllowanceCrowdsale {
    constructor(
        uint256 rate,
        address payable wallet,
        IERC20 token,
        address tokenWallet  // <- new argument
    )
        AllowanceCrowdsale(tokenWallet)  // <- used here
        Crowdsale(rate, wallet, token)
        public
    {

:computer: Environment

Truffle v5.4.33 - a development framework for Ethereum
VS Code
Solidity 0.8.11 for ERC20
Solidity 0.5.1 for Crowdsale

Solved it.
I was calling buyTokens or invoking fallback before approving an allowance for the contracts token wallet.

Fixed it by first adding tokens to _tokenWallet and then approving crowdsale to sell those tokens.

for some reason it would not work if deployerAccount and _tokenWallet were the same address.